├── .editorconfig ├── .gitattributes ├── .github └── workflows │ └── build.yml ├── .gitignore ├── Directory.Build.props ├── LICENSE ├── NuGet.config ├── README.md ├── Samples.sln ├── docs ├── BASICCONCEPTS.md └── screenshots │ ├── SCREENSHOTS.md │ ├── docking.png │ ├── drawing.png │ ├── sdf_shapes.png │ ├── sdf_text.png │ ├── text_shapes.png │ ├── ui_basic.png │ ├── ui_experimental.png │ ├── ui_extended.png │ ├── ui_extended2.png │ └── ui_layouts.png ├── licenses ├── NanoGUI.LICENSE.txt ├── NanoUI.LICENSE.txt ├── NanoVG.LICENSE.txt └── README.md ├── samples ├── FNAExample │ ├── ConvertUtils.cs │ ├── FNAExample.csproj │ ├── FNARenderer.Resources.cs │ ├── FNARenderer.Textures.cs │ ├── FNARenderer.cs │ ├── InputMappings.cs │ ├── NanoUIGame.cs │ ├── Program.cs │ ├── README.md │ └── Resources │ │ ├── Effect.fx │ │ ├── Effect.fxb │ │ └── Macros.fxh ├── MonoGameExample │ ├── InputMappings.cs │ ├── MGRenderer.Resources.cs │ ├── MGRenderer.Textures.cs │ ├── MGRenderer.cs │ ├── MonoGameExample.csproj │ ├── NanoUIGame.cs │ ├── Program.cs │ └── Resources │ │ ├── Effect.dx11.mgfxo │ │ ├── Effect.fx │ │ ├── Effect.ogl.mgfxo │ │ └── Macros.fxh ├── NanoUIDemos │ ├── Assets │ │ ├── fonts │ │ │ ├── Inconsolata-Regular.ttf │ │ │ ├── NotoSansTC-Regular.ttf │ │ │ ├── Roboto-Black.ttf │ │ │ ├── Roboto-BlackItalic.ttf │ │ │ ├── Roboto-Bold.ttf │ │ │ ├── Roboto-BoldItalic.ttf │ │ │ ├── Roboto-Italic.ttf │ │ │ ├── Roboto-Light.ttf │ │ │ ├── Roboto-LightItalic.ttf │ │ │ ├── Roboto-Medium.ttf │ │ │ ├── Roboto-MediumItalic.ttf │ │ │ ├── Roboto-Regular.ttf │ │ │ ├── Roboto-Thin.ttf │ │ │ ├── Roboto-ThinItalic.ttf │ │ │ ├── fa-brands-400.ttf │ │ │ ├── fa-regular-400.ttf │ │ │ └── fa-solid-900.ttf │ │ ├── images │ │ │ ├── image0.png │ │ │ ├── image1.png │ │ │ ├── image10.png │ │ │ ├── image11.png │ │ │ ├── image2.png │ │ │ ├── image3.png │ │ │ ├── image4.png │ │ │ ├── image5.png │ │ │ ├── image6.png │ │ │ ├── image7.png │ │ │ ├── image8.png │ │ │ └── image9.png │ │ ├── landscape.png │ │ ├── shaders │ │ │ ├── NanoUI.frag.glsl │ │ │ ├── NanoUI.frag.hlsl │ │ │ ├── NanoUI.vert.glsl │ │ │ └── NanoUI.vert.hlsl │ │ └── svg │ │ │ ├── decoration.svg │ │ │ ├── testBasic.svg │ │ │ ├── text.svg │ │ │ ├── tiger.svg │ │ │ └── tiger2.svg │ ├── DemoAssets.cs │ ├── DemoBase.cs │ ├── DemoFactory.cs │ ├── Drawing │ │ ├── DrawingDemo.cs │ │ ├── SDFTextDemo.cs │ │ ├── SvgDemo.cs │ │ └── TextShapesDemo.cs │ ├── Experimental │ │ ├── Components │ │ │ ├── Editors │ │ │ │ ├── BoolEditor.cs │ │ │ │ ├── ColorEditor.cs │ │ │ │ ├── EnumEditor.cs │ │ │ │ ├── IPropertyEditor.cs │ │ │ │ ├── NumericEditor.cs │ │ │ │ ├── StringEditor.cs │ │ │ │ ├── StructEditor.cs │ │ │ │ └── Vector2Editor.cs │ │ │ ├── UIDial.cs │ │ │ ├── UIGraph.cs │ │ │ ├── UIPerformanceGraph.cs │ │ │ ├── UIPropertyGrid.cs │ │ │ ├── UIPropertyGridCategoryPanel.cs │ │ │ ├── UIRoundMeter.cs │ │ │ ├── UISearchBox.cs │ │ │ ├── UISeparator.cs │ │ │ ├── UISwitchBox.cs │ │ │ ├── UITextArea.cs │ │ │ └── UIToleranceBar.cs │ │ ├── ThemeEXT.cs │ │ └── Utils │ │ │ └── CategorySorter.cs │ ├── NanoUIDemos.csproj │ ├── PerfGraph.cs │ └── UI │ │ ├── UIBasicDemo.cs │ │ ├── UIDockingDemo.cs │ │ ├── UIExperimentalDemo.cs │ │ ├── UIExtended2Demo.cs │ │ ├── UIExtendedDemo.cs │ │ └── UILayoutDemo.cs ├── README.md └── VeldridExample │ ├── InputMappings.cs │ ├── Program.cs │ ├── README.md │ ├── Shaders │ ├── VeldridShaderCreator.cs │ ├── shader.frag │ └── shader.vert │ ├── VeldridExample.csproj │ ├── VeldridRenderer.Resources.cs │ ├── VeldridRenderer.Textures.cs │ └── VeldridRenderer.cs └── source ├── Common ├── ArrayBuffer.cs ├── Brushes.cs ├── Category.cs ├── Color.cs ├── ColumnDefinition.cs ├── CornerRadius.cs ├── Enums.cs ├── FontAwesomeIcon.cs ├── GlyphPosition.cs ├── GlyphShapeCommand.cs ├── Inputs.cs ├── MinMax.cs ├── Paint.cs ├── Rect.cs ├── Shortcut.cs ├── TextRow.cs ├── TextureDesc.cs ├── Thickness.cs └── UnsafeBuffer.cs ├── Components ├── Bars │ ├── UIMenubar.cs │ ├── UITitlebar.cs │ ├── UIToolbar.cs │ └── UIWidgetbar.cs ├── Buttons │ ├── UINumericUpDown.cs │ ├── UIToolButton.cs │ └── UIUpDownButton.cs ├── Colors │ ├── UIAlphaBar.cs │ ├── UIColorPicker.cs │ └── UIColorWheel.cs ├── Dialogs │ ├── UIColorDialog.cs │ ├── UIDialog.cs │ ├── UIFileDialog.cs │ ├── UIFolderDialog.cs │ ├── UIMessageBox.cs │ └── UIMultilineMessageBox.cs ├── Docking │ ├── DockArea.cs │ ├── DockContainer.cs │ ├── DockHit.cs │ ├── DockNode.Methods.cs │ ├── DockNode.cs │ ├── DockTabWidget.cs │ ├── DockTitlebar.cs │ └── DockWindow.cs ├── Files │ ├── FileFolderInfo.cs │ ├── UIFileFolderDetails.cs │ ├── UIFileFolderDropdown.cs │ ├── UIFileFolderFlow.cs │ ├── UIFileFolderList.cs │ └── UIFileFolderTree.cs ├── Menus │ ├── UIContextMenu.cs │ ├── UIMenu.cs │ ├── UIMenuItem.cs │ ├── UIMenuSeparator.cs │ └── UIMenuSubmenu.cs ├── Scrolling │ ├── IScrollable.cs │ ├── UIScrollPanel.cs │ └── UIScrollbar.cs ├── Simple │ ├── UIFileIcon.cs │ ├── UIFileIconText.cs │ ├── UIIcon.cs │ ├── UIIconText.cs │ ├── UIImage.cs │ ├── UISeparator.cs │ ├── UIShortcut.cs │ └── UIText.cs ├── UIButton.cs ├── UICheckBox.cs ├── UICollapsablePanel.cs ├── UIComboBox.cs ├── UIEnumDropDown.cs ├── UIGrid.cs ├── UIGridView.cs ├── UIImageViewer.cs ├── UILabel.cs ├── UIListBox.cs ├── UINumericTextBox.cs ├── UIPanel.cs ├── UIPopup.cs ├── UIPopupButton.cs ├── UIProgressbar.cs ├── UIScrollableLabel.cs ├── UISlider.cs ├── UISpinner.cs ├── UISplitPanel.cs ├── UISplitter.cs ├── UISvgWidget.cs ├── UITabWidget.cs ├── UITextField.cs ├── UITooltip.cs ├── UIWidget.cs ├── UIWidgetEXT.cs ├── UIWindow.cs ├── Views │ ├── IViewItem.cs │ ├── Items │ │ ├── FlowItem.cs │ │ ├── RowItem.cs │ │ └── TreeItem.cs │ ├── UIDropDownView.cs │ ├── UIFlowView.cs │ ├── UIListView.cs │ ├── UITableView.cs │ ├── UITreeView.cs │ ├── UIViewItemWidget.cs │ ├── UIViewPanel.cs │ └── UIViewWidget.cs └── WidgetList.cs ├── Fonts ├── Data │ ├── Font.cs │ ├── FontAtlas.cs │ ├── FontAtlasNode.cs │ ├── FontEnums.cs │ ├── FontState.cs │ ├── FontTextIter.cs │ ├── Glyph.cs │ └── GlyphQuad.cs ├── Fontstash.Atlas.cs ├── Fontstash.Extended.cs ├── Fontstash.Utils.cs ├── Fontstash.cs ├── SafeStbTrueTypeManager.cs ├── SafeTrueType │ ├── Bmp.cs │ ├── Buf.cs │ ├── CharStringContext.cs │ ├── Common.cs │ ├── FakePtr.cs │ ├── FontInfo.cs │ └── RectPackContext.cs ├── StbTrueTypeManager.cs └── TrueType │ ├── CRuntime.cs │ ├── StbTrueType.Bitmap.cs │ ├── StbTrueType.Buf.cs │ ├── StbTrueType.CharString.cs │ ├── StbTrueType.Common.cs │ ├── StbTrueType.FontInfo.cs │ ├── StbTrueType.Heap.cs │ ├── StbTrueType.OldRasterizer.cs │ ├── StbTrueType.RectPack.cs │ └── StbTrueType.cs ├── Globals.cs ├── IFontManager.cs ├── INvgRenderer.cs ├── Layouts ├── FlowLayout.cs ├── GridLayout.cs ├── GroupLayout.cs ├── Layout.cs ├── SplitLayout.cs └── StackLayout.cs ├── NanoUI.csproj ├── Nvg ├── Data │ ├── NvgBounds.cs │ ├── NvgParams.cs │ ├── NvgPath.cs │ ├── NvgPathCommand.cs │ ├── NvgPathCommandType.cs │ ├── NvgPoint.cs │ ├── NvgPointFlags.cs │ ├── NvgScissor.cs │ └── NvgState.cs ├── NvgContext.Paths.cs ├── NvgContext.Svg.cs ├── NvgContext.Text.cs ├── NvgContext.Utils.cs └── NvgContext.cs ├── README.md ├── Rendering ├── Data │ ├── DrawCall.cs │ ├── DrawCommand.cs │ ├── FillStrokeInfo.cs │ ├── FragmentUniform.cs │ └── Vertex.cs ├── DrawCache.Commands.cs ├── DrawCache.Indices.cs ├── DrawCache.cs └── DrawEnums.cs ├── Serializers ├── JsonBaseConverters.cs ├── JsonNumericsConverters.cs ├── NumericsArrayConverter.cs └── ThemeSerializer.cs ├── Styles ├── BorderStyle.cs ├── CommonStyle.cs ├── DockingStyle.cs ├── FilesStyle.cs ├── FontsStyle.cs ├── PointerStyle.cs ├── ScrollbarStyle.cs └── WindowStyle.cs ├── Svg ├── Data │ ├── SvgGradientStop.cs │ ├── SvgLinearGradient.cs │ ├── SvgPath.cs │ ├── SvgRadialGradient.cs │ ├── SvgShape.cs │ ├── SvgStyle.cs │ ├── SvgXmlAttribute.cs │ ├── SvgXmlElement.cs │ ├── SvgXmlPath.cs │ ├── SvgXmlPathCommand.cs │ ├── SvgXmlPathCommandType.cs │ └── SvgXmlPoints.cs ├── SvgArcUtils.cs ├── SvgManager.Attributes.cs ├── SvgManager.Create.cs ├── SvgManager.PathCommands.cs ├── SvgManager.cs └── SvgXmlUtils.cs ├── UIScreen.cs ├── UITheme.cs └── Utils ├── ConvertUtils.cs ├── DefaultTheme.cs ├── MathUtils.cs ├── MemoryUtils.cs └── StringUtils.cs /.editorconfig: -------------------------------------------------------------------------------- 1 | ; This file is for unifying the coding style for different editors and IDEs. 2 | ; More information at http://EditorConfig.org 3 | 4 | root = true 5 | 6 | [*] 7 | end_of_line = CRLF 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | indent_style = space 11 | 12 | # Xml project files 13 | [*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj,props,targets}] 14 | indent_size = 2 15 | 16 | [*.cs] 17 | indent_size = 4 18 | 19 | # Avoid "this." and "Me." if not necessary 20 | dotnet_style_qualification_for_field = false:suggestion 21 | dotnet_style_qualification_for_property = false:suggestion 22 | dotnet_style_qualification_for_method = false:suggestion 23 | dotnet_style_qualification_for_event = false:suggestion 24 | 25 | # Use language keywords instead of framework type names for type references 26 | dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion 27 | dotnet_style_predefined_type_for_member_access = true:suggestion 28 | 29 | # Don't use "var" 30 | csharp_style_var_for_built_in_types = false:suggestion 31 | csharp_style_var_when_type_is_apparent = false:suggestion 32 | csharp_style_var_elsewhere = false:suggestion 33 | 34 | # Newline settings 35 | csharp_new_line_before_open_brace = all 36 | csharp_new_line_before_else = true 37 | csharp_new_line_before_catch = true 38 | csharp_new_line_before_finally = true 39 | csharp_new_line_before_members_in_object_initializers = true 40 | csharp_new_line_before_members_in_anonymous_types = true 41 | 42 | # License header 43 | file_header_template = Copyright (c) Klaus Bergius.\nLicensed under the MIT License (MIT). 44 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Autodetect text files and ensure that we normalise their 2 | # line endings to lf internally. When checked out they may 3 | # use different line endings. 4 | * text=auto 5 | 6 | # Check out with crlf (Windows) line endings 7 | *.sln text eol=crlf 8 | *.csproj text eol=crlf 9 | *.cs text diff=csharp eol=crlf 10 | *.resx text eol=crlf 11 | *.vsixmanifest text eol=crlf 12 | packages.config text eol=crlf 13 | App.config text eol=crlf 14 | *.bat text eol=crlf 15 | *.cmd text eol=crlf 16 | *.snippet text eol=crlf 17 | *.manifest text eol=crlf 18 | *.licenseheader text eol=crlf 19 | 20 | # Check out with lf (UNIX) line endings 21 | *.sh text eol=lf 22 | .gitignore text eol=lf 23 | .gitattributes text eol=lf 24 | *.md text eol=lf 25 | .travis.yml text eol=lf -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a .NET project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net 3 | 4 | name: Build 5 | 6 | on: 7 | push: 8 | branches: [ "main" ] 9 | paths-ignore: 10 | - '*.md' 11 | - 'LICENSE' 12 | - 'docs/**' 13 | - 'samples/FNAExample/**' 14 | pull_request: 15 | branches: [ "main" ] 16 | paths-ignore: 17 | - '*.md' 18 | - 'LICENSE' 19 | - 'docs/**' 20 | - 'samples/FNAExample/**' 21 | 22 | jobs: 23 | build: 24 | 25 | runs-on: ubuntu-latest 26 | 27 | steps: 28 | - uses: actions/checkout@v4 29 | - name: Setup .NET 30 | uses: actions/setup-dotnet@v4 31 | with: 32 | dotnet-version: 8.0.x 33 | - name: Restore dependencies 34 | run: dotnet restore 35 | - name: Build 36 | run: dotnet build --no-restore 37 | - name: Test 38 | run: dotnet test --no-build --verbosity normal 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## A streamlined .gitignore for modern .NET projects 2 | ## including temporary files, build results, and 3 | ## files generated by popular .NET tools. If you are 4 | ## developing with Visual Studio, the VS .gitignore 5 | ## https://github.com/github/gitignore/blob/main/VisualStudio.gitignore 6 | ## has more thorough IDE-specific entries. 7 | ## 8 | ## Get latest from https://github.com/github/gitignore/blob/main/Dotnet.gitignore 9 | 10 | # Build results 11 | [Dd]ebug/ 12 | [Dd]ebugPublic/ 13 | [Rr]elease/ 14 | [Rr]eleases/ 15 | x64/ 16 | x86/ 17 | [Ww][Ii][Nn]32/ 18 | [Aa][Rr][Mm]/ 19 | [Aa][Rr][Mm]64/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | [Ll]ogs/ 25 | 26 | # .NET Core 27 | project.lock.json 28 | project.fragment.lock.json 29 | artifacts/ 30 | 31 | # ASP.NET Scaffolding 32 | ScaffoldingReadMe.txt 33 | 34 | # NuGet Packages 35 | *.nupkg 36 | # NuGet Symbol Packages 37 | *.snupkg 38 | 39 | # Others 40 | ~$* 41 | *~ 42 | CodeCoverage/ 43 | 44 | # MSBuild Binary and Structured Log 45 | *.binlog 46 | 47 | # MSTest test Results 48 | [Tt]est[Rr]esult*/ 49 | [Bb]uild[Ll]og.* 50 | 51 | # NUnit 52 | *.VisualState.xml 53 | TestResult.xml 54 | nunit-*.xml 55 | *.vs 56 | -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | Debug 4 | $(MSBuildThisFileDirectory) 5 | 6 | 7 | 8 | https://github.com/kbergius/NanoUI 9 | git 10 | true 11 | 12 | 13 | 14 | true 15 | 12.0 16 | disable 17 | enable 18 | true 19 | strict 20 | preview 21 | $(MSBuildThisFileDirectory)NuGet.config 22 | true 23 | true 24 | 25 | 26 | 27 | Klaus Bergius 28 | Copyright (c) Klaus Bergius 29 | NanoUI 30 | MIT 31 | true 32 | $(RepositoryUrl) 33 | dotnet gui ui drawing csharp graphics cross-platform 34 | 35 | 36 | 37 | 38 | portable 39 | true 40 | true 41 | true 42 | snupkg 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Klaus Bergius 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, 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, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NuGet.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Samples.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.3.32922.545 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{E94958C3-5109-4384-A9CB-E165EC5754CE}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NanoUIDemos", "samples\NanoUIDemos\NanoUIDemos.csproj", "{33CED804-2C8B-4E40-9B38-98E513572D8B}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NanoUI", "source\NanoUI.csproj", "{5430BD17-1224-4E66-8A3F-FC15556CC869}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VeldridExample", "samples\VeldridExample\VeldridExample.csproj", "{FF71A6C9-56DB-4263-E789-D6FF34659985}" 13 | EndProject 14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MonoGameExample", "samples\MonoGameExample\MonoGameExample.csproj", "{D171D21B-9B60-2DEC-8329-7A3671366BA8}" 15 | EndProject 16 | Global 17 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 18 | Debug|Any CPU = Debug|Any CPU 19 | Release|Any CPU = Release|Any CPU 20 | EndGlobalSection 21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 22 | {33CED804-2C8B-4E40-9B38-98E513572D8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {33CED804-2C8B-4E40-9B38-98E513572D8B}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {33CED804-2C8B-4E40-9B38-98E513572D8B}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {33CED804-2C8B-4E40-9B38-98E513572D8B}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {5430BD17-1224-4E66-8A3F-FC15556CC869}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {5430BD17-1224-4E66-8A3F-FC15556CC869}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {5430BD17-1224-4E66-8A3F-FC15556CC869}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {5430BD17-1224-4E66-8A3F-FC15556CC869}.Release|Any CPU.Build.0 = Release|Any CPU 30 | {FF71A6C9-56DB-4263-E789-D6FF34659985}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {FF71A6C9-56DB-4263-E789-D6FF34659985}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {FF71A6C9-56DB-4263-E789-D6FF34659985}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {FF71A6C9-56DB-4263-E789-D6FF34659985}.Release|Any CPU.Build.0 = Release|Any CPU 34 | {D171D21B-9B60-2DEC-8329-7A3671366BA8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {D171D21B-9B60-2DEC-8329-7A3671366BA8}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {D171D21B-9B60-2DEC-8329-7A3671366BA8}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {D171D21B-9B60-2DEC-8329-7A3671366BA8}.Release|Any CPU.Build.0 = Release|Any CPU 38 | EndGlobalSection 39 | GlobalSection(SolutionProperties) = preSolution 40 | HideSolutionNode = FALSE 41 | EndGlobalSection 42 | GlobalSection(NestedProjects) = preSolution 43 | {33CED804-2C8B-4E40-9B38-98E513572D8B} = {E94958C3-5109-4384-A9CB-E165EC5754CE} 44 | {FF71A6C9-56DB-4263-E789-D6FF34659985} = {E94958C3-5109-4384-A9CB-E165EC5754CE} 45 | {D171D21B-9B60-2DEC-8329-7A3671366BA8} = {E94958C3-5109-4384-A9CB-E165EC5754CE} 46 | EndGlobalSection 47 | GlobalSection(ExtensibilityGlobals) = postSolution 48 | SolutionGuid = {08FE0BD8-4253-419D-9FBF-7F9671C9AAC2} 49 | EndGlobalSection 50 | EndGlobal 51 | -------------------------------------------------------------------------------- /docs/BASICCONCEPTS.md: -------------------------------------------------------------------------------- 1 | This document is a brief introduction to the basic concepts of the NanoUI. You can find in the code, how the things described here are implemented. 2 | 3 | 4 | # Drawing Layer 5 | 6 | This is the real engine in the NanoUI. It handles all commands passed to it and creates valid draw commands. When you are going to render the ui, you just loop through draw commands that are created and get vertices, indices and uniforms, that you send to your graphics engine. 7 | 8 | Functions in drawing layer are mostly internal and you should consider it as a black box. 9 | 10 | You issue commands to the drawing layer through its API, that is in **NvgContext** class. 11 | 12 | 13 | # UI layer 14 | 15 | UI layer is the extendable part of the NanoUI. It basically consist widgets, that are arranged in tree. 16 | 17 | ### UIWidget 18 | **UIWidget** is the base class in UI layer. It provides most common properties and functions. All other widgets are derived from this component. 19 | When you extend/override functionality in this class, you probably should also call same function in the base class, since there is often functionality that needs to be executed to keep UI layer consistent. 20 | 21 | ### WidgetList 22 | **WidgetList** is a special class, that holds all (parent) widget's childs. There are some helper methods to interact with this list, but the most important part is the order of child widgets in this list: 23 | 24 | - when NanoUI tries to find which child widget should handle user input (mouse, keyboard events etc), it loops the list **backwards** (from the last to the first) 25 | - in drawing phase the looping is done **forwards** (from the first to the last). This ensures that you can have any kind of transparency with overlapping widgets. 26 | 27 | **WidgetList** is a property in **UIWidget** class, so every widget can act as a container or layer. 28 | 29 | ### UIWindow 30 | **UIWindow** is the only widget that provides the opportunity to reposition and resize widget with mouse dragging. 31 | 32 | ### UIScreen 33 | **UIScreen** is the root widget in the widget tree. It is itself also derived from the **UIWidget**, but its main purpose is to orchestrate all the widgets in the widget tree. When you want to send user input events to NanoUI, you send the event to the **UIScreen** and it passes it to the correct widget. 34 | 35 | # Layouting 36 | 37 | NanoUI uses relative positioning system and uses top-left coordinate as position "anchor". This means that every widget knows only their position in their parent's space. So for example position (0, 0) means that widget is positioned in the top-left corner of its parent space. 38 | 39 | Layouts are defined in separate classes and can be dynamically changed at the runtime. When you attach specific layout class to the widget, you must also call either **PerformLayout** or **RequestLayoutUpdate** in order to really process layout calculations and set child widgets' positions & sizes. 40 | 41 | # Theming / styling 42 | 43 | NanoUI uses dynamic styling. The **UITheme** class, that consists all styling information is only stored in the **UIScreen**. When widget wants to use its styling property, it must ask that from the **UIScreen's** theme. 44 | 45 | However you can "hard code" styling information also directly to any individual widget. 46 | -------------------------------------------------------------------------------- /docs/screenshots/SCREENSHOTS.md: -------------------------------------------------------------------------------- 1 | ![Drawing](drawing.png) 2 | 3 | ![SDF shapes](sdf_shapes.png) 4 | 5 | ![SDF text](sdf_text.png) 6 | 7 | ![Text shapes](text_shapes.png) 8 | 9 | ![UI basic](ui_basic.png) 10 | 11 | ![UI extended](ui_extended.png) 12 | 13 | ![UI extended2](ui_extended2.png) 14 | 15 | ![UI experimental](ui_experimental.png) 16 | 17 | ![UI layouts](ui_layouts.png) 18 | -------------------------------------------------------------------------------- /docs/screenshots/docking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbergius/NanoUI/ee700f8af4140ba4cfd8a271da005be57d4eb389/docs/screenshots/docking.png -------------------------------------------------------------------------------- /docs/screenshots/drawing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbergius/NanoUI/ee700f8af4140ba4cfd8a271da005be57d4eb389/docs/screenshots/drawing.png -------------------------------------------------------------------------------- /docs/screenshots/sdf_shapes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbergius/NanoUI/ee700f8af4140ba4cfd8a271da005be57d4eb389/docs/screenshots/sdf_shapes.png -------------------------------------------------------------------------------- /docs/screenshots/sdf_text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbergius/NanoUI/ee700f8af4140ba4cfd8a271da005be57d4eb389/docs/screenshots/sdf_text.png -------------------------------------------------------------------------------- /docs/screenshots/text_shapes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbergius/NanoUI/ee700f8af4140ba4cfd8a271da005be57d4eb389/docs/screenshots/text_shapes.png -------------------------------------------------------------------------------- /docs/screenshots/ui_basic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbergius/NanoUI/ee700f8af4140ba4cfd8a271da005be57d4eb389/docs/screenshots/ui_basic.png -------------------------------------------------------------------------------- /docs/screenshots/ui_experimental.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbergius/NanoUI/ee700f8af4140ba4cfd8a271da005be57d4eb389/docs/screenshots/ui_experimental.png -------------------------------------------------------------------------------- /docs/screenshots/ui_extended.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbergius/NanoUI/ee700f8af4140ba4cfd8a271da005be57d4eb389/docs/screenshots/ui_extended.png -------------------------------------------------------------------------------- /docs/screenshots/ui_extended2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbergius/NanoUI/ee700f8af4140ba4cfd8a271da005be57d4eb389/docs/screenshots/ui_extended2.png -------------------------------------------------------------------------------- /docs/screenshots/ui_layouts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbergius/NanoUI/ee700f8af4140ba4cfd8a271da005be57d4eb389/docs/screenshots/ui_layouts.png -------------------------------------------------------------------------------- /licenses/NanoGUI.LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 Wenzel Jakob , All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this 7 | list of conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | 3. Neither the name of the copyright holder nor the names of its contributors 14 | may be used to endorse or promote products derived from this software 15 | without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | You are under no obligation whatsoever to provide any bug fixes, patches, or 29 | upgrades to the features, functionality or performance of the source code 30 | ("Enhancements") to anyone; however, if you choose to make your Enhancements 31 | available either publicly, or directly to the author of this software, without 32 | imposing a separate written license agreement for such Enhancements, then you 33 | hereby grant the following license: a non-exclusive, royalty-free perpetual 34 | license to install, use, modify, prepare derivative works, incorporate into 35 | other computer software, distribute, and sublicense such enhancements or 36 | derivative works thereof, in binary and source code form. 37 | -------------------------------------------------------------------------------- /licenses/NanoUI.LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Klaus Bergius 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, 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, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /licenses/NanoVG.LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Mikko Mononen memon@inside.org 2 | 3 | This software is provided 'as-is', without any express or implied 4 | warranty. In no event will the authors be held liable for any damages 5 | arising from the use of this software. 6 | 7 | Permission is granted to anyone to use this software for any purpose, 8 | including commercial applications, and to alter it and redistribute it 9 | freely, subject to the following restrictions: 10 | 11 | 1. The origin of this software must not be misrepresented; you must not 12 | claim that you wrote the original software. If you use this software 13 | in a product, an acknowledgment in the product documentation would be 14 | appreciated but is not required. 15 | 2. Altered source versions must be plainly marked as such, and must not be 16 | misrepresented as being the original software. 17 | 3. This notice may not be removed or altered from any source distribution. 18 | 19 | -------------------------------------------------------------------------------- /licenses/README.md: -------------------------------------------------------------------------------- 1 | **NanoUI** is MIT licensed. 2 | 3 | **NanoUI** consists modified ports from the [NanoVG](https://github.com/memononen/nanovg) library (**Zlib** license). 4 | 5 | Additionally **NanoUI's** UI layer is more or less inspired by [NanoGUI](https://github.com/wjakob/nanogui) (**BSD style** license). 6 | 7 | Also **NanoUI** consists a port of the [StbTrueType](https://github.com/nothings/stb) (**Public Domain** license). 8 | 9 | --- 10 | 11 | All these licenses are permissive, free, open-source and commercial-friendly software licenses. 12 | -------------------------------------------------------------------------------- /samples/FNAExample/ConvertUtils.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xna.Framework; 2 | using System.Runtime.CompilerServices; 3 | 4 | namespace FNAExample 5 | { 6 | internal static class ConvertUtils 7 | { 8 | public static Vector2 ToFna(this System.Numerics.Vector2 v) 9 | { 10 | return Unsafe.As(ref v); 11 | } 12 | 13 | public static Vector4 ToFna(this System.Numerics.Vector4 v) 14 | { 15 | return Unsafe.As(ref v); 16 | } 17 | 18 | public static Matrix ToFna(this System.Numerics.Matrix4x4 m) 19 | { 20 | return Unsafe.As(ref m); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /samples/FNAExample/FNAExample.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net8.0 6 | disable 7 | enable 8 | True 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /samples/FNAExample/Program.cs: -------------------------------------------------------------------------------- 1 | namespace FNAExample 2 | { 3 | internal class Program 4 | { 5 | static void Main(string[] args) 6 | { 7 | using (var game = new NanoUIGame()) 8 | { 9 | game.Run(); 10 | } 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /samples/FNAExample/README.md: -------------------------------------------------------------------------------- 1 | FNA example doesn't run out-of-box, since FNA doesn't provide official NuGet. You have to setup it first. 2 | 3 | **Note:** I had to add NPE hack in the beginning of the **SDL3_FNAPlatform.PollEvents** method 4 | 5 | ```cs 6 | if(textInputControlDown == null) 7 | { 8 | textInputControlDown = new bool[7]; 9 | } 10 | ``` 11 | 12 | There could be better solution. 13 | -------------------------------------------------------------------------------- /samples/FNAExample/Resources/Effect.fx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbergius/NanoUI/ee700f8af4140ba4cfd8a271da005be57d4eb389/samples/FNAExample/Resources/Effect.fx -------------------------------------------------------------------------------- /samples/FNAExample/Resources/Effect.fxb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbergius/NanoUI/ee700f8af4140ba4cfd8a271da005be57d4eb389/samples/FNAExample/Resources/Effect.fxb -------------------------------------------------------------------------------- /samples/FNAExample/Resources/Macros.fxh: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // Macros.fxh 3 | // 4 | // Microsoft XNA Community Game Platform 5 | // Copyright (C) Microsoft Corporation. All rights reserved. 6 | //----------------------------------------------------------------------------- 7 | 8 | #ifdef SM4 9 | 10 | // Macros for targetting shader model 4.0 (DX11) 11 | 12 | #define TECHNIQUE(name, vsname, psname ) \ 13 | technique name { pass { VertexShader = compile vs_4_0_level_9_1 vsname (); PixelShader = compile ps_4_0_level_9_1 psname(); } } 14 | 15 | #define BEGIN_CONSTANTS cbuffer Parameters : register(b0) { 16 | #define MATRIX_CONSTANTS 17 | #define END_CONSTANTS }; 18 | 19 | #define _vs(r) 20 | #define _ps(r) 21 | #define _cb(r) 22 | 23 | #define DECLARE_TEXTURE(Name, index) \ 24 | Texture2D Name : register(t##index); \ 25 | sampler Name##Sampler : register(s##index) 26 | 27 | #define DECLARE_CUBEMAP(Name, index) \ 28 | TextureCube Name : register(t##index); \ 29 | sampler Name##Sampler : register(s##index) 30 | 31 | #define SAMPLE_TEXTURE(Name, texCoord) Name.Sample(Name##Sampler, texCoord) 32 | #define SAMPLE_CUBEMAP(Name, texCoord) Name.Sample(Name##Sampler, texCoord) 33 | 34 | 35 | #else 36 | 37 | 38 | // Macros for targetting shader model 2.0 (DX9) 39 | 40 | #define TECHNIQUE(name, vsname, psname ) \ 41 | technique name { pass { VertexShader = compile vs_2_0 vsname (); PixelShader = compile ps_2_0 psname(); } } 42 | 43 | #define BEGIN_CONSTANTS 44 | #define MATRIX_CONSTANTS 45 | #define END_CONSTANTS 46 | 47 | #define _vs(r) : register(vs, r) 48 | #define _ps(r) : register(ps, r) 49 | #define _cb(r) 50 | 51 | #define DECLARE_TEXTURE(Name, index) \ 52 | texture2D Name; \ 53 | sampler Name##Sampler : register(s##index) = sampler_state { Texture = (Name); }; 54 | 55 | #define DECLARE_CUBEMAP(Name, index) \ 56 | textureCUBE Name; \ 57 | sampler Name##Sampler : register(s##index) = sampler_state { Texture = (Name); }; 58 | 59 | #define SAMPLE_TEXTURE(Name, texCoord) tex2D(Name##Sampler, texCoord) 60 | #define SAMPLE_CUBEMAP(Name, texCoord) texCUBE(Name##Sampler, texCoord) 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /samples/MonoGameExample/MonoGameExample.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net8.0 6 | disable 7 | enable 8 | True 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /samples/MonoGameExample/Program.cs: -------------------------------------------------------------------------------- 1 | namespace MonoGameExample 2 | { 3 | public class Program 4 | { 5 | static void Main(string[] args) 6 | { 7 | using (var game = new NanoUIGame()) 8 | { 9 | game.Run(); 10 | } 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /samples/MonoGameExample/Resources/Effect.dx11.mgfxo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbergius/NanoUI/ee700f8af4140ba4cfd8a271da005be57d4eb389/samples/MonoGameExample/Resources/Effect.dx11.mgfxo -------------------------------------------------------------------------------- /samples/MonoGameExample/Resources/Effect.fx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbergius/NanoUI/ee700f8af4140ba4cfd8a271da005be57d4eb389/samples/MonoGameExample/Resources/Effect.fx -------------------------------------------------------------------------------- /samples/MonoGameExample/Resources/Effect.ogl.mgfxo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbergius/NanoUI/ee700f8af4140ba4cfd8a271da005be57d4eb389/samples/MonoGameExample/Resources/Effect.ogl.mgfxo -------------------------------------------------------------------------------- /samples/MonoGameExample/Resources/Macros.fxh: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // Macros.fxh 3 | // 4 | // Microsoft XNA Community Game Platform 5 | // Copyright (C) Microsoft Corporation. All rights reserved. 6 | //----------------------------------------------------------------------------- 7 | 8 | #ifdef SM4 9 | 10 | // Macros for targetting shader model 4.0 (DX11) 11 | 12 | #define TECHNIQUE(name, vsname, psname ) \ 13 | technique name { pass { VertexShader = compile vs_4_0_level_9_1 vsname (); PixelShader = compile ps_4_0_level_9_1 psname(); } } 14 | 15 | #define BEGIN_CONSTANTS cbuffer Parameters : register(b0) { 16 | #define MATRIX_CONSTANTS 17 | #define END_CONSTANTS }; 18 | 19 | #define _vs(r) 20 | #define _ps(r) 21 | #define _cb(r) 22 | 23 | #define DECLARE_TEXTURE(Name, index) \ 24 | Texture2D Name : register(t##index); \ 25 | sampler Name##Sampler : register(s##index) 26 | 27 | #define DECLARE_CUBEMAP(Name, index) \ 28 | TextureCube Name : register(t##index); \ 29 | sampler Name##Sampler : register(s##index) 30 | 31 | #define SAMPLE_TEXTURE(Name, texCoord) Name.Sample(Name##Sampler, texCoord) 32 | #define SAMPLE_CUBEMAP(Name, texCoord) Name.Sample(Name##Sampler, texCoord) 33 | 34 | 35 | #else 36 | 37 | 38 | // Macros for targetting shader model 2.0 (DX9) 39 | 40 | #define TECHNIQUE(name, vsname, psname ) \ 41 | technique name { pass { VertexShader = compile vs_2_0 vsname (); PixelShader = compile ps_2_0 psname(); } } 42 | 43 | #define BEGIN_CONSTANTS 44 | #define MATRIX_CONSTANTS 45 | #define END_CONSTANTS 46 | 47 | #define _vs(r) : register(vs, r) 48 | #define _ps(r) : register(ps, r) 49 | #define _cb(r) 50 | 51 | #define DECLARE_TEXTURE(Name, index) \ 52 | texture2D Name; \ 53 | sampler Name##Sampler : register(s##index) = sampler_state { Texture = (Name); }; 54 | 55 | #define DECLARE_CUBEMAP(Name, index) \ 56 | textureCUBE Name; \ 57 | sampler Name##Sampler : register(s##index) = sampler_state { Texture = (Name); }; 58 | 59 | #define SAMPLE_TEXTURE(Name, texCoord) tex2D(Name##Sampler, texCoord) 60 | #define SAMPLE_CUBEMAP(Name, texCoord) texCUBE(Name##Sampler, texCoord) 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /samples/NanoUIDemos/Assets/fonts/Inconsolata-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbergius/NanoUI/ee700f8af4140ba4cfd8a271da005be57d4eb389/samples/NanoUIDemos/Assets/fonts/Inconsolata-Regular.ttf -------------------------------------------------------------------------------- /samples/NanoUIDemos/Assets/fonts/NotoSansTC-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbergius/NanoUI/ee700f8af4140ba4cfd8a271da005be57d4eb389/samples/NanoUIDemos/Assets/fonts/NotoSansTC-Regular.ttf -------------------------------------------------------------------------------- /samples/NanoUIDemos/Assets/fonts/Roboto-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbergius/NanoUI/ee700f8af4140ba4cfd8a271da005be57d4eb389/samples/NanoUIDemos/Assets/fonts/Roboto-Black.ttf -------------------------------------------------------------------------------- /samples/NanoUIDemos/Assets/fonts/Roboto-BlackItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbergius/NanoUI/ee700f8af4140ba4cfd8a271da005be57d4eb389/samples/NanoUIDemos/Assets/fonts/Roboto-BlackItalic.ttf -------------------------------------------------------------------------------- /samples/NanoUIDemos/Assets/fonts/Roboto-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbergius/NanoUI/ee700f8af4140ba4cfd8a271da005be57d4eb389/samples/NanoUIDemos/Assets/fonts/Roboto-Bold.ttf -------------------------------------------------------------------------------- /samples/NanoUIDemos/Assets/fonts/Roboto-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbergius/NanoUI/ee700f8af4140ba4cfd8a271da005be57d4eb389/samples/NanoUIDemos/Assets/fonts/Roboto-BoldItalic.ttf -------------------------------------------------------------------------------- /samples/NanoUIDemos/Assets/fonts/Roboto-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbergius/NanoUI/ee700f8af4140ba4cfd8a271da005be57d4eb389/samples/NanoUIDemos/Assets/fonts/Roboto-Italic.ttf -------------------------------------------------------------------------------- /samples/NanoUIDemos/Assets/fonts/Roboto-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbergius/NanoUI/ee700f8af4140ba4cfd8a271da005be57d4eb389/samples/NanoUIDemos/Assets/fonts/Roboto-Light.ttf -------------------------------------------------------------------------------- /samples/NanoUIDemos/Assets/fonts/Roboto-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbergius/NanoUI/ee700f8af4140ba4cfd8a271da005be57d4eb389/samples/NanoUIDemos/Assets/fonts/Roboto-LightItalic.ttf -------------------------------------------------------------------------------- /samples/NanoUIDemos/Assets/fonts/Roboto-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbergius/NanoUI/ee700f8af4140ba4cfd8a271da005be57d4eb389/samples/NanoUIDemos/Assets/fonts/Roboto-Medium.ttf -------------------------------------------------------------------------------- /samples/NanoUIDemos/Assets/fonts/Roboto-MediumItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbergius/NanoUI/ee700f8af4140ba4cfd8a271da005be57d4eb389/samples/NanoUIDemos/Assets/fonts/Roboto-MediumItalic.ttf -------------------------------------------------------------------------------- /samples/NanoUIDemos/Assets/fonts/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbergius/NanoUI/ee700f8af4140ba4cfd8a271da005be57d4eb389/samples/NanoUIDemos/Assets/fonts/Roboto-Regular.ttf -------------------------------------------------------------------------------- /samples/NanoUIDemos/Assets/fonts/Roboto-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbergius/NanoUI/ee700f8af4140ba4cfd8a271da005be57d4eb389/samples/NanoUIDemos/Assets/fonts/Roboto-Thin.ttf -------------------------------------------------------------------------------- /samples/NanoUIDemos/Assets/fonts/Roboto-ThinItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbergius/NanoUI/ee700f8af4140ba4cfd8a271da005be57d4eb389/samples/NanoUIDemos/Assets/fonts/Roboto-ThinItalic.ttf -------------------------------------------------------------------------------- /samples/NanoUIDemos/Assets/fonts/fa-brands-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbergius/NanoUI/ee700f8af4140ba4cfd8a271da005be57d4eb389/samples/NanoUIDemos/Assets/fonts/fa-brands-400.ttf -------------------------------------------------------------------------------- /samples/NanoUIDemos/Assets/fonts/fa-regular-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbergius/NanoUI/ee700f8af4140ba4cfd8a271da005be57d4eb389/samples/NanoUIDemos/Assets/fonts/fa-regular-400.ttf -------------------------------------------------------------------------------- /samples/NanoUIDemos/Assets/fonts/fa-solid-900.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbergius/NanoUI/ee700f8af4140ba4cfd8a271da005be57d4eb389/samples/NanoUIDemos/Assets/fonts/fa-solid-900.ttf -------------------------------------------------------------------------------- /samples/NanoUIDemos/Assets/images/image0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbergius/NanoUI/ee700f8af4140ba4cfd8a271da005be57d4eb389/samples/NanoUIDemos/Assets/images/image0.png -------------------------------------------------------------------------------- /samples/NanoUIDemos/Assets/images/image1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbergius/NanoUI/ee700f8af4140ba4cfd8a271da005be57d4eb389/samples/NanoUIDemos/Assets/images/image1.png -------------------------------------------------------------------------------- /samples/NanoUIDemos/Assets/images/image10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbergius/NanoUI/ee700f8af4140ba4cfd8a271da005be57d4eb389/samples/NanoUIDemos/Assets/images/image10.png -------------------------------------------------------------------------------- /samples/NanoUIDemos/Assets/images/image11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbergius/NanoUI/ee700f8af4140ba4cfd8a271da005be57d4eb389/samples/NanoUIDemos/Assets/images/image11.png -------------------------------------------------------------------------------- /samples/NanoUIDemos/Assets/images/image2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbergius/NanoUI/ee700f8af4140ba4cfd8a271da005be57d4eb389/samples/NanoUIDemos/Assets/images/image2.png -------------------------------------------------------------------------------- /samples/NanoUIDemos/Assets/images/image3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbergius/NanoUI/ee700f8af4140ba4cfd8a271da005be57d4eb389/samples/NanoUIDemos/Assets/images/image3.png -------------------------------------------------------------------------------- /samples/NanoUIDemos/Assets/images/image4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbergius/NanoUI/ee700f8af4140ba4cfd8a271da005be57d4eb389/samples/NanoUIDemos/Assets/images/image4.png -------------------------------------------------------------------------------- /samples/NanoUIDemos/Assets/images/image5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbergius/NanoUI/ee700f8af4140ba4cfd8a271da005be57d4eb389/samples/NanoUIDemos/Assets/images/image5.png -------------------------------------------------------------------------------- /samples/NanoUIDemos/Assets/images/image6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbergius/NanoUI/ee700f8af4140ba4cfd8a271da005be57d4eb389/samples/NanoUIDemos/Assets/images/image6.png -------------------------------------------------------------------------------- /samples/NanoUIDemos/Assets/images/image7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbergius/NanoUI/ee700f8af4140ba4cfd8a271da005be57d4eb389/samples/NanoUIDemos/Assets/images/image7.png -------------------------------------------------------------------------------- /samples/NanoUIDemos/Assets/images/image8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbergius/NanoUI/ee700f8af4140ba4cfd8a271da005be57d4eb389/samples/NanoUIDemos/Assets/images/image8.png -------------------------------------------------------------------------------- /samples/NanoUIDemos/Assets/images/image9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbergius/NanoUI/ee700f8af4140ba4cfd8a271da005be57d4eb389/samples/NanoUIDemos/Assets/images/image9.png -------------------------------------------------------------------------------- /samples/NanoUIDemos/Assets/landscape.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbergius/NanoUI/ee700f8af4140ba4cfd8a271da005be57d4eb389/samples/NanoUIDemos/Assets/landscape.png -------------------------------------------------------------------------------- /samples/NanoUIDemos/Assets/shaders/NanoUI.frag.glsl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbergius/NanoUI/ee700f8af4140ba4cfd8a271da005be57d4eb389/samples/NanoUIDemos/Assets/shaders/NanoUI.frag.glsl -------------------------------------------------------------------------------- /samples/NanoUIDemos/Assets/shaders/NanoUI.frag.hlsl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbergius/NanoUI/ee700f8af4140ba4cfd8a271da005be57d4eb389/samples/NanoUIDemos/Assets/shaders/NanoUI.frag.hlsl -------------------------------------------------------------------------------- /samples/NanoUIDemos/Assets/shaders/NanoUI.vert.glsl: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(location = 0) in vec2 vertex; 4 | layout(location = 1) in vec2 tcoord; 5 | 6 | layout(set = 0, binding = 0) uniform TransformBuffer 7 | { 8 | mat4 transformMat; 9 | }; 10 | 11 | layout(location = 0) out vec2 fpos; 12 | layout(location = 1) out vec2 ftcoord; 13 | 14 | void main() 15 | { 16 | ftcoord = tcoord; 17 | fpos = vertex; 18 | 19 | gl_Position = transformMat * vec4(vertex, 0, 1); 20 | } -------------------------------------------------------------------------------- /samples/NanoUIDemos/Assets/shaders/NanoUI.vert.hlsl: -------------------------------------------------------------------------------- 1 | cbuffer UniformBlock : register(b0, space1) 2 | { 3 | float4x4 MatrixTransform : packoffset(c0); 4 | }; 5 | 6 | struct Input 7 | { 8 | float2 Position : POSITION; 9 | float2 UV : TEXCOORD0; 10 | }; 11 | 12 | struct Output 13 | { 14 | float4 Position : SV_Position; // vertex position 15 | float2 Ftcoord : TEXCOORD0; // float 2 tex coord 16 | float2 Fpos : TEXCOORD1; // float 2 position 17 | }; 18 | 19 | Output main(Input input) 20 | { 21 | Output output; 22 | output.Position = mul(MatrixTransform, float4(input.Position, 0, 1.0f)); 23 | output.Ftcoord = input.UV; 24 | output.Fpos = input.Position; 25 | 26 | return output; 27 | } 28 | -------------------------------------------------------------------------------- /samples/NanoUIDemos/Assets/svg/decoration.svg: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 14 | 15 | -------------------------------------------------------------------------------- /samples/NanoUIDemos/Assets/svg/text.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 33 | 34 | -------------------------------------------------------------------------------- /samples/NanoUIDemos/DemoAssets.cs: -------------------------------------------------------------------------------- 1 | using NanoUI; 2 | using NanoUIDemos.Experimental; 3 | using NanoUI.Nvg; 4 | using NanoUI.Styles; 5 | 6 | namespace NanoUIDemos 7 | { 8 | // Note: images are in PNG format (maximum portability). 9 | internal static class DemoAssets 10 | { 11 | public const string FontNormal = "./Assets/fonts/Roboto-Regular.ttf"; 12 | public const string FontBold = "./Assets/fonts/Roboto-Bold.ttf"; 13 | public const string FontIcons = "./Assets/fonts/fa-solid-900.ttf"; 14 | 15 | static int[] _textures = new int[12]; 16 | 17 | public static int[] Textures => _textures; 18 | 19 | static int _testTexture; 20 | 21 | public static int TestTexture => _testTexture; 22 | 23 | public static void InitTestTextures(NvgContext ctx) 24 | { 25 | for (uint i = 0; i < 12; i++) 26 | { 27 | string file = "./Assets/images/image" + i + ".png"; 28 | _textures[i] = ctx.CreateTexture(file); 29 | 30 | if (_textures[i] < 0) 31 | { 32 | Console.Error.WriteLine("Could not load " + file); 33 | Environment.Exit(-1); 34 | } 35 | } 36 | 37 | // test image 38 | string file2 = "./Assets/landscape.png"; 39 | _testTexture = ctx.CreateTexture(file2); 40 | } 41 | 42 | public static UITheme GetTheme(NvgContext ctx) 43 | { 44 | // init fonts 45 | FontsStyle fonts = new FontsStyle() 46 | { 47 | DefaultFontType = "Normal", 48 | DefaultIconsType = "Icons", 49 | }; 50 | 51 | fonts.FontTypes.Add("Normal", FontNormal); 52 | fonts.FontTypes.Add("Bold", FontBold); 53 | fonts.FontTypes.Add("Icons", FontIcons); 54 | 55 | // create default theme 56 | ThemeEXT theme = UITheme.CreateDefault(ctx, fonts); 57 | theme.Save("DefaultTheme.json"); 58 | 59 | // create 60 | ThemeEXT? loadTheme = UITheme.Load(ctx, "DefaultTheme.json"); 61 | 62 | if(loadTheme != null) 63 | { 64 | loadTheme.PopulateExt(); 65 | 66 | // this is just for testing 67 | InitTestTextures(ctx); 68 | 69 | return loadTheme; 70 | } 71 | 72 | // this is just for testing 73 | InitTestTextures(ctx); 74 | 75 | return theme; 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /samples/NanoUIDemos/DemoFactory.cs: -------------------------------------------------------------------------------- 1 | using NanoUI; 2 | using NanoUI.Common; 3 | using NanoUIDemos.Drawing; 4 | using NanoUI.Nvg; 5 | using NanoUIDemos.UI; 6 | using System.Numerics; 7 | 8 | namespace NanoUIDemos 9 | { 10 | public enum DemoType 11 | { 12 | Docking, 13 | Drawing, 14 | SDFText, 15 | SvgShapes, 16 | TextShapes, 17 | UIBasic, 18 | UIExtended, 19 | UIExtended2, 20 | UIExperimental, 21 | UILayouts 22 | } 23 | 24 | public static class DemoFactory 25 | { 26 | public static DemoBase CreateDemo(NvgContext ctx, DemoType demoType, Vector2 windoSize) 27 | { 28 | if(demoType == DemoType.Drawing) 29 | { 30 | // no screen 31 | return new DrawingDemo(ctx, windoSize); 32 | } 33 | else if (demoType == DemoType.TextShapes) 34 | { 35 | // no screen 36 | return new TextShapesDemo(ctx, windoSize); 37 | } 38 | else if (demoType == DemoType.SDFText) 39 | { 40 | // no screen 41 | return new SDFTextDemo(ctx, windoSize); 42 | } 43 | else if (demoType == DemoType.SvgShapes) 44 | { 45 | // no screen 46 | return new SvgDemo(ctx, windoSize); 47 | } 48 | 49 | // get default theme 50 | var theme = DemoAssets.GetTheme(ctx); 51 | 52 | // create screen 53 | var screen = new UIScreen(theme, windoSize); 54 | screen.BackgroundFocused = screen.BackgroundUnfocused = new SolidBrush(new Color(0.3f, 0.3f, 0.32f, 1.0f)); 55 | 56 | switch (demoType) 57 | { 58 | case DemoType.Docking: 59 | return new UIDockingDemo(screen); 60 | case DemoType.UIExperimental: 61 | return new UIExperimentalDemo(screen); 62 | case DemoType.UIExtended: 63 | return new UIExtendedDemo(screen, ctx); 64 | case DemoType.UIExtended2: 65 | return new UIExtended2Demo(screen); 66 | case DemoType.UILayouts: 67 | return new UILayoutDemo(screen); 68 | default: 69 | return new UIBasicDemo(screen); 70 | } 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /samples/NanoUIDemos/Drawing/SvgDemo.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Nvg; 2 | using System.Numerics; 3 | 4 | namespace NanoUIDemos.Drawing 5 | { 6 | public class SvgDemo : DemoBase 7 | { 8 | // note: these are needed for performance stats 9 | public static int _fontNormal, _fontBold; 10 | 11 | private readonly NvgContext _ctx; 12 | Vector2 _windoSize; 13 | 14 | static int _svgShape; 15 | static int _svgTextShape; 16 | 17 | public SvgDemo(NvgContext ctx, Vector2 windowSize) 18 | :base(null) 19 | { 20 | _ctx = ctx; 21 | _windoSize = windowSize; 22 | 23 | // note: there is no theme - so we manually load fonts 24 | _fontNormal = _ctx.CreateFont("normal", DemoAssets.FontNormal); 25 | if (_fontNormal == -1) 26 | { 27 | Console.Error.WriteLine("Could not add font regular."); 28 | Environment.Exit(-1); 29 | } 30 | _fontBold = _ctx.CreateFont("bold", DemoAssets.FontBold); 31 | if (_fontBold == -1) 32 | { 33 | Console.Error.WriteLine("Could not add font bold."); 34 | Environment.Exit(-1); 35 | } 36 | 37 | // init test textures 38 | //DemoAssets.InitTestTextures(_ctx); 39 | 40 | // Create svg 41 | string path = "./Assets/svg/tiger.svg"; 42 | //path = "./Assets/svg/testBasic.svg"; 43 | 44 | _svgShape = ctx.CreateSvg(path); 45 | 46 | _svgTextShape = ctx.CreateSvg("./Assets/svg/text.svg"); 47 | } 48 | 49 | #region Inputs 50 | 51 | public override bool OnPointerMove(Vector2 pointerPos, Vector2 rel) 52 | { 53 | return false; 54 | } 55 | 56 | #endregion 57 | 58 | float _deltaSeconds; 59 | float t; 60 | public override void Update(float deltaSeconds) 61 | { 62 | _deltaSeconds = deltaSeconds; 63 | t += deltaSeconds; 64 | } 65 | 66 | public override void ScreenResize(Vector2 size, NvgContext ctx) 67 | { 68 | _windoSize = size; 69 | } 70 | 71 | float scale = 0.5f; 72 | float sign = 1; 73 | public override void Draw(NvgContext ctx) 74 | { 75 | if(_svgShape >= 0) 76 | { 77 | if (scale > 2.5f) 78 | { 79 | sign = -1; 80 | } 81 | else if (scale < 0.5f) 82 | { 83 | sign = 1; 84 | } 85 | 86 | scale += sign * _deltaSeconds * 0.5f; 87 | 88 | ctx.SaveState(); 89 | 90 | ctx.Scale(scale); 91 | //ctx.Skew(float.DegreesToRadians(30), 0); 92 | //ctx.Translate(new Vector2(_windoSize.X * 0.3f, 100)); 93 | //ctx.Rotate(t, new Vector2(_windoSize.X * 0.3f)); 94 | 95 | _ctx.DrawSvg(_svgShape); 96 | _ctx.DrawSvg(_svgTextShape); 97 | 98 | ctx.RestoreState(); 99 | } 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /samples/NanoUIDemos/Experimental/Components/Editors/BoolEditor.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Components; 2 | using NanoUI.Nvg; 3 | using System.Reflection; 4 | 5 | namespace NanoUIDemos.Experimental.Components.Editors 6 | { 7 | // todo: if we want to have BoolEditor configurable, we should not inherit from widget & 8 | // have CheckBox as child 9 | public class BoolEditor : UICheckBox, IPropertyEditor 10 | { 11 | Func? _getValue; 12 | Action? _setValue; 13 | PropertyInfo? _propertyInfo; 14 | 15 | bool _currentValue; 16 | 17 | public BoolEditor(UIWidget parent) 18 | : base(parent, string.Empty) 19 | { 20 | // todo : in CheckBox 21 | IconExtraScale = 0.8f; 22 | } 23 | 24 | public void InitEditor( 25 | PropertyInfo propertyInfo, 26 | Func getValue, 27 | Action setValue) 28 | { 29 | _getValue = getValue; 30 | _setValue = setValue; 31 | _propertyInfo = propertyInfo; 32 | 33 | CheckedChanged += (val) => 34 | { 35 | _currentValue = val; 36 | 37 | setValue?.Invoke(propertyInfo, val); 38 | }; 39 | } 40 | 41 | public override void Draw(NvgContext ctx) 42 | { 43 | // if used as combined field, func & property info are null 44 | if (_getValue != null && _propertyInfo != null) 45 | { 46 | SetValue(_getValue.Invoke(_propertyInfo)); 47 | } 48 | 49 | base.Draw(ctx); 50 | } 51 | 52 | // call from Draw 53 | void SetValue(object? value) 54 | { 55 | if (value == null) 56 | return; 57 | 58 | // parse 59 | if (bool.TryParse(value.ToString(), out bool val)) 60 | { 61 | if (_currentValue == val) 62 | return; 63 | 64 | _currentValue = val; 65 | 66 | Checked = val; 67 | } 68 | } 69 | 70 | // we use prorotype editor when creating new editor 71 | public IPropertyEditor Clone(UIWidget parent) 72 | { 73 | return new BoolEditor(parent); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /samples/NanoUIDemos/Experimental/Components/Editors/EnumEditor.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Components; 2 | using NanoUI.Nvg; 3 | using System.Reflection; 4 | 5 | namespace NanoUIDemos.Experimental.Components.Editors 6 | { 7 | public class EnumEditor : UIEnumDropDown, IPropertyEditor 8 | where TEnum :struct, Enum 9 | { 10 | Func? _getValue; 11 | Action? _setValue; 12 | PropertyInfo? _propertyInfo; 13 | 14 | TEnum _currentValue = default; 15 | 16 | public EnumEditor(UIWidget parent) 17 | : base(parent) 18 | { 19 | 20 | } 21 | 22 | public void InitEditor( 23 | PropertyInfo propertyInfo, 24 | Func getValue, 25 | Action setValue) 26 | { 27 | _getValue = getValue; 28 | _setValue = setValue; 29 | _propertyInfo = propertyInfo; 30 | 31 | SelectedChanged += (val) => 32 | { 33 | _currentValue = val; 34 | setValue?.Invoke(propertyInfo, val); 35 | }; 36 | } 37 | 38 | public override void Draw(NvgContext ctx) 39 | { 40 | // if used as combined field, func & property info are null 41 | if (_getValue != null && _propertyInfo != null) 42 | { 43 | SetValue(_getValue.Invoke(_propertyInfo)); 44 | } 45 | 46 | base.Draw(ctx); 47 | } 48 | 49 | // call from Draw 50 | void SetValue(object? value) 51 | { 52 | if (value == null) 53 | return; 54 | 55 | TEnum val = (TEnum)value; 56 | 57 | if (_currentValue.Equals(val)) 58 | return; 59 | 60 | _currentValue = val; 61 | 62 | SetSelected(val); 63 | } 64 | 65 | // we use prorotype editor when creating new editor 66 | public IPropertyEditor Clone(UIWidget parent) 67 | { 68 | return new EnumEditor(parent); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /samples/NanoUIDemos/Experimental/Components/Editors/IPropertyEditor.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Components; 2 | using System.Reflection; 3 | 4 | namespace NanoUIDemos.Experimental.Components.Editors 5 | { 6 | public interface IPropertyEditor 7 | { 8 | // we set id so user can search for spesific editor in propertygrid 9 | // id is set to 10 | string? Name { get; internal set; } 11 | 12 | void InitEditor(PropertyInfo propertyInfo, 13 | Func getValue, 14 | Action setValue); 15 | 16 | // we use prorotype editor when creating new editor (no need to Activator.CreateInstance) 17 | // parent is set to category content panel 18 | IPropertyEditor Clone(UIWidget parent); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /samples/NanoUIDemos/Experimental/Components/Editors/StringEditor.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Components; 2 | using NanoUI.Nvg; 3 | using System.Reflection; 4 | 5 | namespace NanoUIDemos.Experimental.Components.Editors 6 | { 7 | // todo: if we want to have StringEditor configurable, we should not inherit from widget & 8 | // have TextBox as child 9 | public class StringEditor : UITextField, IPropertyEditor 10 | { 11 | Func? _getValue; 12 | Action? _setValue; 13 | PropertyInfo? _propertyInfo; 14 | 15 | string _currentValue = string.Empty; 16 | 17 | public StringEditor(UIWidget parent) 18 | : base(parent) 19 | { 20 | Editable = true; 21 | } 22 | 23 | public void InitEditor( 24 | PropertyInfo propertyInfo, 25 | Func getValue, 26 | Action setValue) 27 | { 28 | _getValue = getValue; 29 | _setValue = setValue; 30 | _propertyInfo = propertyInfo; 31 | 32 | // wrap change 33 | TextChanged += (val) => 34 | { 35 | _currentValue = val; 36 | setValue?.Invoke(propertyInfo, val); 37 | }; 38 | } 39 | 40 | public override void Draw(NvgContext ctx) 41 | { 42 | // if used as combined field, func & property info are null 43 | if (_getValue != null && _propertyInfo != null) 44 | { 45 | SetValue(_getValue.Invoke(_propertyInfo)); 46 | } 47 | 48 | base.Draw(ctx); 49 | } 50 | 51 | // call from Draw 52 | void SetValue(object? value) 53 | { 54 | if (value == null) 55 | return; 56 | 57 | string? str = value.ToString(); 58 | 59 | if (str == null || _currentValue == str) 60 | return; 61 | 62 | _currentValue = str; 63 | 64 | Text = str; 65 | } 66 | 67 | // we use prorotype editor when creating new editor 68 | public IPropertyEditor Clone(UIWidget parent) 69 | { 70 | return new StringEditor(parent); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /samples/NanoUIDemos/Experimental/Components/Editors/StructEditor.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Common; 2 | using NanoUI.Components; 3 | using System; 4 | using System.Numerics; 5 | using System.Reflection; 6 | 7 | namespace NanoUIDemos.Experimental.Components.Editors 8 | { 9 | // TODO : Handle structs by creating new category in propertygrid & looping properties 10 | /*public class StructEditor : Widget, IPropertyEditor 11 | { 12 | public StructEditor() 13 | : base(null) 14 | { 15 | GridLayout layout = 16 | new GridLayout(Orientation.Horizontal, 2, 17 | LayoutAlignment.Maximum, 0, 5); 18 | 19 | // set columns (label, widget) 20 | //layout.SetColAlignment(new LayoutAlignment[] { LayoutAlignment.Maximum, LayoutAlignment.Fill }); 21 | layout.SetColAlignment(new LayoutAlignment[] { LayoutAlignment.Minimum, LayoutAlignment.Fill }); 22 | // SETS TO ALIGN TOP 23 | layout.SetRowAlignment(LayoutAlignment.Minimum); 24 | 25 | layout.SetSpacing(0, 10); 26 | Layout = layout; 27 | } 28 | 29 | //public void SetValue(PropertyInfo propertyInfo, object? obj) 30 | public void SetValue(PropertyInfo propertyInfo, object? value, Action action) 31 | 32 | { 33 | if (value == null) 34 | return; 35 | 36 | 37 | } 38 | 39 | public override void Draw(NvgContext ctx) 40 | { 41 | base.Draw(ctx); 42 | } 43 | }*/ 44 | } 45 | -------------------------------------------------------------------------------- /samples/NanoUIDemos/Experimental/Components/UIPropertyGridCategoryPanel.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Components; 2 | using NanoUIDemos.Experimental.Components.Editors; 3 | using NanoUI.Nvg; 4 | using System.Numerics; 5 | 6 | namespace NanoUIDemos.Experimental.Components 7 | { 8 | public class UIPropertyGridCategoryPanel : UICollapsablePanel 9 | { 10 | // Parent is UIPropertyGridPanel 11 | // We must get labels with there 12 | UIPropertyGridPanel _propertyGridPanel; 13 | 14 | public UIPropertyGridCategoryPanel(UIPropertyGridPanel widget) 15 | : base(widget) 16 | { 17 | _propertyGridPanel = widget; 18 | } 19 | 20 | #region Events 21 | 22 | public override void OnCollapseChanged(bool collapsed) 23 | { 24 | Content.Visible = !collapsed; 25 | 26 | // This should be because we must find vscroll 27 | if (Parent != null && Parent.Parent != null) 28 | { 29 | RequestLayoutUpdate(Parent.Parent); 30 | } 31 | else 32 | { 33 | RequestLayoutUpdate(Parent); 34 | } 35 | } 36 | 37 | #endregion 38 | 39 | #region Layout 40 | 41 | public override void PerformLayout(NvgContext ctx) 42 | { 43 | // set fixed width so we can calculate editors widths 44 | if(Parent != null) 45 | FixedSize = new Vector2(Parent.Size.X, 0); 46 | 47 | // set editor widths 48 | foreach (var child in Content.Children.AsReadOnlySpan()) 49 | { 50 | if (child is IPropertyEditor) 51 | { 52 | child.FixedSize = new Vector2(GetEditorWidth(), 0); 53 | } 54 | } 55 | 56 | base.PerformLayout(ctx); 57 | } 58 | 59 | int GetEditorWidth() 60 | { 61 | // todo: magical numbers 62 | return (int)(FixedSize.X - _propertyGridPanel.LabelsWidth - 20); 63 | } 64 | 65 | #endregion 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /samples/README.md: -------------------------------------------------------------------------------- 1 | **Note:** You can view other demos changing **_demoType** in: 2 | 3 | * **Program.cs** (VeldridExample) 4 | * **NanoUIGame.cs** (MonoGameExample & FNAExample) 5 | -------------------------------------------------------------------------------- /samples/VeldridExample/README.md: -------------------------------------------------------------------------------- 1 | This example uses several external libraries: 2 | 3 | - [Silk.NET](https://github.com/dotnet/Silk.NET) 4 | - [Veldrid](https://github.com/veldrid/veldrid) 5 | - [StbImageSharp](https://github.com/StbSharp/StbImageSharp) 6 | 7 | See licences from the external sites. 8 | -------------------------------------------------------------------------------- /samples/VeldridExample/Shaders/shader.frag: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbergius/NanoUI/ee700f8af4140ba4cfd8a271da005be57d4eb389/samples/VeldridExample/Shaders/shader.frag -------------------------------------------------------------------------------- /samples/VeldridExample/Shaders/shader.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(location = 0) in vec2 vertex; 4 | layout(location = 1) in vec2 tcoord; 5 | 6 | layout(set = 0, binding = 0) uniform TransformBuffer 7 | { 8 | mat4 transformMat; 9 | }; 10 | 11 | layout(location = 0) out vec2 fpos; 12 | layout(location = 1) out vec2 ftcoord; 13 | 14 | void main() 15 | { 16 | ftcoord = tcoord; 17 | fpos = vertex; 18 | 19 | gl_Position = transformMat * vec4(vertex, 0, 1); 20 | } -------------------------------------------------------------------------------- /samples/VeldridExample/VeldridExample.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | VeldridExample 6 | disable 7 | VeldridExample 8 | net8.0 9 | True 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | Never 20 | 21 | 22 | Never 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /source/Common/Category.cs: -------------------------------------------------------------------------------- 1 | namespace NanoUI.Common 2 | { 3 | /// 4 | /// Used in property grid to combine properties to categories 5 | /// 6 | public struct Category 7 | { 8 | /// 9 | /// Category id. 10 | /// 11 | public string Id; 12 | 13 | /// 14 | /// DisplayText. 15 | /// 16 | public string DisplayText; 17 | 18 | /// 19 | /// SortKey. Used in sorting. 20 | /// 21 | public int SortKey; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /source/Common/ColumnDefinition.cs: -------------------------------------------------------------------------------- 1 | namespace NanoUI.Common 2 | { 3 | // todo: alignments 4 | 5 | /// 6 | /// ColumnDefinition. 7 | /// 8 | public struct ColumnDefinition 9 | { 10 | /// 11 | /// Width. 12 | /// 13 | public int Width { get; set; } 14 | 15 | /// 16 | /// Stretch. 17 | /// 18 | public bool Stretch { get; set; } 19 | 20 | /// 21 | /// Ctor, 22 | /// 23 | /// Width 24 | /// Stretch 25 | public ColumnDefinition(int width, bool stretch = false) 26 | { 27 | Width = width; 28 | Stretch = stretch; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /source/Common/CornerRadius.cs: -------------------------------------------------------------------------------- 1 | namespace NanoUI.Common 2 | { 3 | /// 4 | /// CornerRadius. 5 | /// 6 | public struct CornerRadius 7 | { 8 | /// 9 | /// TopLeft. 10 | /// 11 | public float TopLeft { get; set; } 12 | 13 | /// 14 | /// TopRight. 15 | /// 16 | public float TopRight { get; set; } 17 | 18 | /// 19 | /// BottomLeft. 20 | /// 21 | public float BottomLeft { get; set; } 22 | 23 | /// 24 | /// BottomRight. 25 | /// 26 | public float BottomRight { get; set; } 27 | 28 | /// 29 | /// Ctor. 30 | /// 31 | /// Rounding all corners 32 | public CornerRadius(float rounding) 33 | { 34 | TopLeft = TopRight = BottomLeft = BottomRight = rounding; 35 | } 36 | 37 | /// 38 | /// Ctor. 39 | /// 40 | /// TopLeft 41 | /// TopRight 42 | /// BottomLeft 43 | /// BottomRight 44 | public CornerRadius(float topLeft, float topRight, float bottomLeft, float bottomRight) 45 | { 46 | TopLeft = topLeft; 47 | TopRight = topRight; 48 | BottomLeft = bottomLeft; 49 | BottomRight = bottomRight; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /source/Common/GlyphPosition.cs: -------------------------------------------------------------------------------- 1 | namespace NanoUI.Common 2 | { 3 | /// 4 | /// GlyphPosition. 5 | /// 6 | public struct GlyphPosition 7 | { 8 | /// 9 | /// Index of glyph in passed text 10 | /// 11 | public int Index; 12 | 13 | /// 14 | /// The X-coordinate of the logical glyph position 15 | /// 16 | public float X; 17 | 18 | /// 19 | /// The smallest X-bound of the glyph shape 20 | /// 21 | public float MinX; 22 | 23 | /// 24 | /// The largest X-bound of the glyph shape 25 | /// 26 | public float MaxX; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /source/Common/GlyphShapeCommand.cs: -------------------------------------------------------------------------------- 1 | using System.Numerics; 2 | 3 | namespace NanoUI.Common 4 | { 5 | /// 6 | /// GlyphShapeCommand is used when glyphs are rendered with shapes. 7 | /// 8 | public struct GlyphShapeCommand 9 | { 10 | // note: in drawText converts this to NvgPathCommand 11 | // note2: 12 | // - MoveTo uses P0 13 | // - LineTo uses P0 14 | // - BezierTo uses P0 & P1 & P2 (P0 & P1 are control points, P2 is end point) 15 | // - QuadTo uses P0 as control point, P1 as endpoint 16 | // note3: this is nearly identical to NvgPathCommand, but we don't use it since it is internal 17 | 18 | /// 19 | /// GlyphShapeCommandType 20 | /// 21 | public GlyphShapeCommandType CommandType; 22 | 23 | /// 24 | /// Position0 25 | /// 26 | public Vector2 P0; 27 | 28 | /// 29 | /// Position1 30 | /// 31 | public Vector2 P1; 32 | 33 | /// 34 | /// Position2 35 | /// 36 | public Vector2 P2; 37 | 38 | /// 39 | /// Winding 40 | /// 41 | public Winding Winding; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /source/Common/MinMax.cs: -------------------------------------------------------------------------------- 1 | namespace NanoUI.Common 2 | { 3 | // todo : check Min < Max 4 | 5 | /// 6 | /// MinMax. 7 | /// 8 | public struct MinMax 9 | { 10 | /// 11 | /// Min 12 | /// 13 | public float Min { get; set; } 14 | 15 | /// 16 | /// Max 17 | /// 18 | public float Max { get; set; } 19 | 20 | /// 21 | /// Ctor. 22 | /// 23 | public MinMax() { } 24 | 25 | /// 26 | /// Ctor. 27 | /// 28 | /// Min 29 | /// Max 30 | public MinMax(float min, float max) 31 | { 32 | Min = min; 33 | Max = max; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /source/Common/Shortcut.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace NanoUI.Common 4 | { 5 | /// 6 | /// Shortcut for menus. 7 | /// 8 | public struct Shortcut 9 | { 10 | /// 11 | /// Key. Default Key.Unknown. 12 | /// 13 | public Key Key; 14 | 15 | /// 16 | /// KeyModifiers. Default KeyModifiers.None. 17 | /// 18 | public KeyModifiers Modifiers; 19 | 20 | /// 21 | /// Ctor. 22 | /// 23 | /// Key 24 | public Shortcut(Key key) 25 | { 26 | Key = key; 27 | } 28 | 29 | /// 30 | /// Ctor. 31 | /// 32 | /// Key 33 | /// Key modifiers 34 | public Shortcut(Key key, KeyModifiers modifiers) 35 | { 36 | Key = key; 37 | Modifiers = modifiers; 38 | } 39 | 40 | /// 41 | /// Checks if Key + KeyModifiers match shortcut. 42 | /// 43 | /// Key 44 | /// Key modifiers 45 | /// Success 46 | public bool Match(Key key, KeyModifiers modifiers) 47 | { 48 | return key == Key && Modifiers == modifiers; 49 | } 50 | 51 | public override string ToString() 52 | { 53 | // not set 54 | if (Key == Key.Unknown) 55 | return string.Empty; 56 | 57 | StringBuilder sb = new StringBuilder(); 58 | 59 | // control 60 | if ((Modifiers & KeyModifiers.Control) != 0) 61 | { 62 | sb.Append("Ctrl+"); 63 | } 64 | 65 | // alt 66 | if ((Modifiers & KeyModifiers.Alt) != 0) 67 | { 68 | sb.Append("Alt+"); 69 | } 70 | 71 | // shift 72 | if ((Modifiers & KeyModifiers.Shift) != 0) 73 | { 74 | sb.Append("Shift+"); 75 | } 76 | 77 | // key 78 | sb.Append(Key.ToString()); 79 | 80 | return sb.ToString(); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /source/Common/TextRow.cs: -------------------------------------------------------------------------------- 1 | namespace NanoUI.Common 2 | { 3 | /// 4 | /// TextRow. 5 | /// 6 | public struct TextRow 7 | { 8 | /// 9 | /// Row start in passed text 10 | /// 11 | public int StartPos; 12 | 13 | /// 14 | /// Text length in row 15 | /// 16 | public int TextLength; 17 | 18 | /// 19 | /// Logical width of the row 20 | /// 21 | public float Width; 22 | 23 | /// 24 | /// Actual least X-bound of the row. Logical with and bounds can differ 25 | /// because of kerning and some parts over extending 26 | /// 27 | public float MinX; 28 | 29 | /// 30 | /// Actual largest X-bound of the row. Logical with and bounds can differ 31 | /// because of kerning and some parts over extending. 32 | /// 33 | public float MaxX; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /source/Common/TextureDesc.cs: -------------------------------------------------------------------------------- 1 | namespace NanoUI.Common 2 | { 3 | /// 4 | /// Texture description is passed to renderer when creating textures. 5 | /// 6 | public struct TextureDesc 7 | { 8 | /// 9 | /// Width. 10 | /// 11 | public uint Width; 12 | 13 | /// 14 | /// Height. 15 | /// 16 | public uint Height; 17 | 18 | /// 19 | /// TextureFormat should be mapped to graphic engine's pixel format. 20 | /// 21 | public TextureFormat Format; 22 | 23 | /// 24 | /// TextureFlags. Optional flags to pass. 25 | /// 26 | public TextureFlags TextureFlags; 27 | 28 | /// 29 | /// Creates TextureDesc. 30 | /// 31 | /// Width 32 | /// Height 33 | /// TextureFormat 34 | /// TextureFlags 35 | public TextureDesc(uint width, uint height, TextureFormat format = TextureFormat.RGBA, TextureFlags flags = 0) 36 | { 37 | Width = width; 38 | Height = height; 39 | Format = format; 40 | TextureFlags = flags; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /source/Common/Thickness.cs: -------------------------------------------------------------------------------- 1 | namespace NanoUI.Common 2 | { 3 | // note: this could be a Vector2, but for possible future use could be extended 4 | // (separate left/right & top/bottom values) 5 | 6 | /// 7 | /// Thickness is used as layouting hints (margins, paddings etc). 8 | /// 9 | public struct Thickness 10 | { 11 | /// 12 | /// Horizontal. 13 | /// 14 | public float Horizontal { get; set; } 15 | 16 | /// 17 | /// Vertical. 18 | /// 19 | public float Vertical { get; set; } 20 | 21 | /// 22 | /// Creates. 23 | /// 24 | /// Horizontal & vertical value 25 | public Thickness(float val) 26 | { 27 | Horizontal = Vertical = val; 28 | } 29 | 30 | /// 31 | /// Creates. 32 | /// 33 | /// Horizontal 34 | /// Vertical 35 | public Thickness(float horizontal, float vertical) 36 | { 37 | Horizontal = horizontal; 38 | Vertical = vertical; 39 | } 40 | 41 | /// 42 | /// Gets value by index 43 | /// 44 | /// Index 45 | /// Value 46 | public float this[int index] 47 | { 48 | get => index == 1? Vertical : Horizontal; 49 | set 50 | { 51 | if(index == 1) 52 | Vertical = value; 53 | else 54 | Horizontal = value; 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /source/Components/Bars/UIMenubar.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Common; 2 | using NanoUI.Nvg; 3 | 4 | namespace NanoUI.Components.Bars 5 | { 6 | // note: this doesn't pass OnKeyChar event to children, because widget needs to be in focuspath 7 | 8 | /// 9 | /// UIMenubar is only widget, that can receive OnKeyUpDown event while not in UIScreen's focus path. 10 | /// 11 | /// 12 | /// To get this OnKeyUpDown event parent of this (screen or window) must be in focus path (screen is allways). 13 | /// The main use case is to pass keyboard shortcuts to menu items. 14 | /// This means also that you can have multiple windows with menubars, but only the active/focused 15 | /// window gets event and passes it to this widget. 16 | /// 17 | public class UIMenubar : UIWidgetbar 18 | { 19 | /// 20 | public UIMenubar() { } 21 | 22 | /// 23 | public UIMenubar(UIWidget parent) 24 | : base(parent) 25 | { 26 | DisablePointerFocus = true; 27 | } 28 | 29 | #region Events 30 | 31 | /// 32 | public override bool OnKeyUpDown(Key key, bool down, KeyModifiers modifiers) 33 | { 34 | foreach (var child in Children.AsReadOnlySpan()) 35 | { 36 | if (!child.Visible || child.Disabled) 37 | continue; 38 | 39 | if (child.OnKeyUpDown(key, down, modifiers)) 40 | { 41 | return true; 42 | } 43 | } 44 | 45 | return base.OnKeyUpDown(key, down, modifiers); 46 | } 47 | 48 | #endregion 49 | 50 | #region Drawing 51 | 52 | /// 53 | public override void Draw(NvgContext ctx) 54 | { 55 | DrawBackgroundBrush(ctx); 56 | 57 | base.Draw(ctx); 58 | 59 | this.DrawBorder(ctx, false); 60 | } 61 | 62 | #endregion 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /source/Components/Bars/UIToolbar.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Nvg; 2 | 3 | namespace NanoUI.Components.Bars 4 | { 5 | /// 6 | /// UIToolbar. 7 | /// 8 | public class UIToolbar : UIWidgetbar 9 | { 10 | /// 11 | public UIToolbar() { } 12 | 13 | /// 14 | public UIToolbar(UIWidget parent) 15 | : base(parent) 16 | { 17 | DisablePointerFocus = true; 18 | } 19 | 20 | #region Drawing 21 | 22 | /// 23 | public override void Draw(NvgContext ctx) 24 | { 25 | DrawBackgroundBrush(ctx); 26 | 27 | base.Draw(ctx); 28 | 29 | this.DrawBorder(ctx, false); 30 | } 31 | 32 | #endregion 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /source/Components/Bars/UIWidgetbar.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Common; 2 | using NanoUI.Layouts; 3 | using NanoUI.Nvg; 4 | using System.Numerics; 5 | 6 | namespace NanoUI.Components.Bars 7 | { 8 | // todo: statusbar (needs positioning info: top / bottom) 9 | // todo2: if also Scrollbar extends this, there should be also "right" (needs to refactor scrollbar logic!) 10 | 11 | /// 12 | /// UIWidgetbar is an abstract bar component, that provides some common functionalty. 13 | /// It could be used for example in UIWindow with other specified bars (menubar, toolbar, etc). 14 | /// 15 | /// 16 | /// Parent should do the positioning, since it knows what bars there exist. 17 | /// 18 | public abstract class UIWidgetbar : UIWidget 19 | { 20 | /// 21 | public UIWidgetbar() 22 | { 23 | 24 | } 25 | 26 | /// 27 | public UIWidgetbar(UIWidget parent) 28 | :base(parent) 29 | { 30 | // todo: configurable? 31 | ChildrenLayout = new StackLayout(Orientation.Horizontal, LayoutAlignment.Middle); 32 | ChildrenLayout.Spacing = new Vector2 (5, 0); 33 | 34 | ThemeType = GetType(); 35 | } 36 | 37 | #region Layout 38 | 39 | /// 40 | public override void PerformLayout(NvgContext ctx) 41 | { 42 | base.PerformLayout(ctx); 43 | 44 | // stretch 45 | if(Parent != null) 46 | Size = new Vector2(Parent.Size.X, Size.Y); 47 | } 48 | 49 | #endregion 50 | 51 | #region Events 52 | 53 | /// 54 | public override void OnScreenResize(Vector2 size, NvgContext ctx) 55 | { 56 | // note: this should be unnecessary, since other parents should not call this function 57 | if(Parent is not UIScreen screen) 58 | return; 59 | 60 | Size = new Vector2(size.X, Size.Y); 61 | 62 | PerformLayout(ctx); 63 | } 64 | 65 | #endregion 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /source/Components/Buttons/UIToolButton.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Common; 2 | using NanoUI.Nvg; 3 | using System.Numerics; 4 | 5 | namespace NanoUI.Components.Buttons 6 | { 7 | /// 8 | /// UIToolButton can be combined with button flags RadioButton & ToggleButton. 9 | /// 10 | /// There is no text by default. 11 | public class UIToolButton : UIButton 12 | { 13 | /// 14 | public UIToolButton() 15 | { 16 | // set defaults to theme impl - prevents circular reference 17 | Dimension = default; 18 | Flags = default; 19 | 20 | // no text! 21 | } 22 | 23 | /// 24 | public UIToolButton(UIWidget parent) 25 | : this(parent, -1) 26 | { 27 | } 28 | 29 | /// 30 | public UIToolButton(UIWidget parent, int icon) 31 | : base(parent, string.Empty, icon) 32 | { 33 | // no text! 34 | ThemeType = typeof(UIButton); 35 | } 36 | 37 | #region Properties 38 | 39 | uint? _dimension; 40 | 41 | /// 42 | /// Dimension 43 | /// 44 | public uint Dimension 45 | { 46 | get => _dimension?? GetTheme().ToolButton.Dimension; 47 | set => _dimension = value; 48 | } 49 | 50 | ButtonFlags? _flags; 51 | 52 | /// 53 | public override ButtonFlags Flags 54 | { 55 | get => _flags?? GetTheme().ToolButton.Flags; 56 | set => _flags = value; 57 | } 58 | 59 | #endregion 60 | 61 | #region Layout 62 | 63 | /// 64 | public override Vector2 PreferredSize(NvgContext ctx) 65 | { 66 | return Vector2.Max(MinSize, new Vector2(Dimension)); 67 | } 68 | 69 | #endregion 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /source/Components/Dialogs/UIDialog.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Common; 2 | using NanoUI.Nvg; 3 | using System.ComponentModel; 4 | 5 | namespace NanoUI.Components.Dialogs 6 | { 7 | /// 8 | /// UIDialog is a base class, that is registered & stored in UIScreen. 9 | /// Use Screen.GetDialog. 10 | /// 11 | public class UIDialog : UIWindow 12 | { 13 | /// 14 | /// Caller is used to set focus back to caller widget when dialog closes. 15 | /// 16 | protected UIWidget? _caller; 17 | 18 | /// 19 | public UIDialog() 20 | { 21 | // set defaults to theme impl - prevents circular reference 22 | _buttonPanelMargin = new(); 23 | } 24 | 25 | /// 26 | protected UIDialog(UIScreen screen) 27 | : base(screen, string.Empty, ScrollbarType.NONE) 28 | { 29 | // init in screen creation, create titlebar with empty string 30 | 31 | Visible = false; 32 | Modal = true; 33 | //TitleAlign = TextHorizontalAlign.Center; 34 | 35 | ThemeType = typeof(UIDialog); 36 | } 37 | 38 | #region Properties 39 | 40 | Thickness? _buttonPanelMargin; 41 | 42 | /// 43 | /// ButtonPanelMargin. 44 | /// 45 | [Category(Globals.CATEGORY_LAYOUT)] 46 | public Thickness ButtonPanelMargin 47 | { 48 | get => _buttonPanelMargin?? GetTheme().Dialog.ButtonPanelMargin; 49 | set => _buttonPanelMargin = value; 50 | } 51 | 52 | #endregion 53 | 54 | #region Methods 55 | 56 | /// 57 | /// ReInit. 58 | /// 59 | protected void ReInit(NvgContext ctx) 60 | { 61 | // center 62 | Center(); 63 | 64 | PerformLayout(ctx); 65 | RequestFocus(); 66 | } 67 | 68 | /// 69 | /// Reset. 70 | /// Note: we use normally same dialog instances, that are stored in UIScreen. 71 | /// When you get dialog from screen, screen calls Reset(), so you can have clean state. 72 | /// 73 | public virtual void Reset() 74 | { 75 | 76 | } 77 | 78 | /// 79 | public override void Close() 80 | { 81 | // note: we override base since it disposes widget 82 | 83 | if (_caller != null) 84 | { 85 | _caller.RequestFocus(); 86 | } 87 | else 88 | { 89 | // todo: get window! 90 | Screen?.RequestFocus(); 91 | } 92 | 93 | Visible = false; 94 | } 95 | 96 | #endregion 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /source/Components/Docking/DockArea.cs: -------------------------------------------------------------------------------- 1 | namespace NanoUI.Components.Docking 2 | { 3 | /// 4 | /// DockArea. Values (int) are used as array indexes. 5 | /// 6 | public enum DockArea : int 7 | { 8 | Left = 0, 9 | Right = 1, 10 | Top = 2, 11 | Bottom = 3, 12 | Center = 4, 13 | NONE = -1 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /source/Components/Docking/DockHit.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Common; 2 | using NanoUI.Nvg; 3 | using System.Numerics; 4 | 5 | namespace NanoUI.Components.Docking 6 | { 7 | /// 8 | /// DockHit. 9 | /// 10 | public struct DockHit 11 | { 12 | /// 13 | /// Hit areas (used also to check hit). 14 | /// 15 | public Rect Area; 16 | 17 | /// 18 | /// Hit areas' fills - visual clue where docking is happening. 19 | /// 20 | public Rect Fill; 21 | 22 | /// 23 | /// Overlay areas - visual clue where docking is happening. 24 | /// 25 | public Rect Overlay; 26 | 27 | /// 28 | /// Contains 29 | /// 30 | /// Position 31 | /// Success 32 | public bool Contains(Vector2 position) => Area.Contains(position); 33 | 34 | /// 35 | /// Draws hit area. 36 | /// 37 | /// NvgContext 38 | /// Corner radius 39 | /// Background color 40 | /// Brush 41 | public void DrawHitArea(NvgContext ctx, float cornerRadius, Color backgroundColor, BrushBase brush) 42 | { 43 | if (Area.Size == Vector2.Zero) 44 | return; 45 | 46 | // background 47 | ctx.BeginPath(); 48 | ctx.RoundedRect(Area, cornerRadius); 49 | ctx.FillColor(backgroundColor); 50 | ctx.Fill(); 51 | 52 | // fill 53 | if (Fill.Size != Vector2.Zero) 54 | { 55 | brush?.Draw(ctx, 56 | Fill.Position, Fill.Size, null); 57 | } 58 | 59 | // we draw border last since it overwrites background & fill 60 | ctx.BeginPath(); 61 | ctx.RoundedRect(Area, cornerRadius); 62 | ctx.StrokeWidth(1); 63 | ctx.StrokeColor(Color.Black); 64 | ctx.Stroke(); 65 | } 66 | 67 | /// 68 | /// Draws overlay. 69 | /// 70 | /// NvgContext 71 | /// Color 72 | public void DrawOverlay(NvgContext ctx, Color color) 73 | { 74 | ctx.BeginPath(); 75 | ctx.Rect(Overlay); 76 | ctx.FillColor(color); 77 | ctx.Fill(); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /source/Components/Docking/DockTabWidget.cs: -------------------------------------------------------------------------------- 1 | namespace NanoUI.Components.Docking 2 | { 3 | // todo: we could have here also some spesific methods (remove from DockNode) 4 | 5 | /// 6 | /// DockTabWidget is an extension of normal tabwidget. 7 | /// So you can have different styling for dock tabs & normal tabwidget. 8 | /// 9 | public class DockTabWidget : UITabWidget 10 | { 11 | /// 12 | public DockTabWidget() 13 | : base() 14 | { 15 | 16 | } 17 | 18 | /// 19 | public DockTabWidget(DockNode parent) 20 | : base(parent) 21 | { 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /source/Components/Docking/DockTitlebar.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Common; 2 | using NanoUI.Components.Bars; 3 | using System.Numerics; 4 | 5 | namespace NanoUI.Components.Docking 6 | { 7 | // todo: there should be easy mechanics to set title etc (probably context menu) 8 | 9 | /// 10 | /// DockTitlebar supports only dock nodes. 11 | /// 12 | public class DockTitlebar : UITitlebar 13 | { 14 | /// 15 | public DockTitlebar() { } 16 | 17 | // todo: dock node name 18 | 19 | /// 20 | public DockTitlebar(DockNode parent) 21 | :base(parent, "DockNode") 22 | { 23 | // todo: in properties 24 | BackgroundFocused = GetTheme().Docks.TitleBackgroundFocused; 25 | BackgroundUnfocused = GetTheme().Docks.TitleBackgroundUnfocused; 26 | 27 | string? fontType = GetTheme().Docks.TitleFontType; 28 | if (fontType != null) 29 | { 30 | FontType = fontType; 31 | } 32 | 33 | FontSize = GetTheme().Docks.TitleFontSize; 34 | ButtonSize = GetTheme().Docks.TitleButtonSize; 35 | } 36 | 37 | #region Events 38 | 39 | /// 40 | public override bool OnPointerUpDown(Vector2 p, PointerButton button, bool down) 41 | { 42 | // check first if titlebar button handle event 43 | if(base.OnPointerUpDown(p, button, down)) 44 | { 45 | return true; 46 | } 47 | 48 | // we handle - pointer on label 49 | // note: we don't need to "clear" drag widget since UIScreen does it automatically when pointer UP 50 | if (down) 51 | { 52 | Screen?.SetDragWidget(this); 53 | } 54 | 55 | return true; 56 | } 57 | 58 | /// 59 | public override bool OnPointerDrag(Vector2 p, Vector2 rel) 60 | { 61 | // check if this is dragged outside title area 62 | 63 | if (!Contains(p)) 64 | { 65 | // remove drag widget, so we can start detach process 66 | Screen?.SetDragWidget(null); 67 | 68 | // signal parent dock node that it should be detached 69 | if (Parent != null && Parent.OnDetach(this)) 70 | { 71 | 72 | } 73 | 74 | return true; 75 | } 76 | 77 | return false; 78 | } 79 | 80 | #endregion 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /source/Components/Files/FileFolderInfo.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Common; 2 | 3 | namespace NanoUI.Components.Files 4 | { 5 | // todo: here could be logic to create display text 6 | 7 | /// 8 | /// FileFolderInfo. 9 | /// 10 | public struct FileFolderInfo 11 | { 12 | /// 13 | /// Path 14 | /// 15 | public string Path; 16 | 17 | /// 18 | /// FileFolderType 19 | /// 20 | public FileFolderType FileFolderType; 21 | 22 | /// 23 | /// Constructor. 24 | /// 25 | /// Path 26 | /// FileFolderType 27 | public FileFolderInfo(string path, FileFolderType fileFolderType) 28 | { 29 | Path = path; 30 | FileFolderType = fileFolderType; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /source/Components/Menus/UIContextMenu.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Common; 2 | using NanoUI.Layouts; 3 | using System; 4 | 5 | namespace NanoUI.Components.Menus 6 | { 7 | // todo: context menu holds menubuttons, but these can't have shortcuts(?) 8 | 9 | /// 10 | /// UIContextMenu. 11 | /// 12 | public class UIContextMenu : UIPopup 13 | { 14 | /// 15 | /// Handles clicks & shortcut matches. This is called from the child menu item buttons, 16 | /// if they have ItemId specified. 17 | /// 18 | public Action? MenuItemSelected; 19 | 20 | UIWidget? _owner; 21 | 22 | /// 23 | public UIContextMenu() { } 24 | 25 | /// 26 | public UIContextMenu(UIWidget owner) 27 | : base(owner.Screen) 28 | { 29 | _owner = owner; 30 | owner.ContextMenu = this; 31 | 32 | ChildrenLayout = new StackLayout(Orientation.Vertical, LayoutAlignment.Minimum); 33 | AnchorSize = 0; 34 | 35 | ThemeType = typeof(UIContextMenu); 36 | } 37 | 38 | #region Events 39 | 40 | /// 41 | /// This is called from menu items, 42 | /// when they are clicked or shortcut matches. 43 | /// 44 | public virtual void OnMenuItemSelected(int menuItemId) 45 | { 46 | // closes this 47 | _owner?.RequestFocus(); 48 | 49 | MenuItemSelected?.Invoke(menuItemId); 50 | } 51 | 52 | /// 53 | public override bool OnFocusChanged(bool focused) 54 | { 55 | // todo: this could be in base Popup? 56 | 57 | if (!Disabled) 58 | { 59 | // set values 60 | Focused = focused; 61 | Visible = focused; 62 | 63 | if (!focused) 64 | { 65 | // close child popups 66 | RecursiveCloseChildPopups(this); 67 | 68 | // set focus to owner 69 | //_owner.RequestFocus(); 70 | // lost focus => propagate to owner 71 | _owner?.FindParentWindow()?.OnFocusChanged(false); 72 | } 73 | else if(Screen != null) 74 | { 75 | // bring window to front 76 | _owner?.FindParentWindow()?.MoveToLast(); 77 | 78 | Position = Screen.PointerPosition; 79 | RequestFocus(); 80 | 81 | // keep owner window focused (this parent is screen) 82 | _owner?.FindParentWindow()?.OnFocusChanged(true); 83 | } 84 | 85 | return true; 86 | } 87 | 88 | return base.OnFocusChanged(focused); 89 | } 90 | 91 | #endregion 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /source/Components/Menus/UIMenuSeparator.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Components.Simple; 2 | using NanoUI.Nvg; 3 | using System.Numerics; 4 | 5 | namespace NanoUI.Components.Menus 6 | { 7 | /// 8 | /// UIMenuSeparator. 9 | /// 10 | public class UIMenuSeparator : UISeparator 11 | { 12 | /// 13 | public UIMenuSeparator(UIWidget parent) 14 | :base(parent) 15 | { 16 | Height = 1; 17 | FixedSize = new Vector2(0, Height); 18 | } 19 | 20 | #region Layout 21 | 22 | /// 23 | public override void PerformLayout(NvgContext ctx) 24 | { 25 | base.PerformLayout(ctx); 26 | 27 | // stretch with parent 28 | if (Parent != null) 29 | Size = new Vector2(Parent.Size.X, Size.Y); 30 | } 31 | 32 | #endregion 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /source/Components/Menus/UIMenuSubmenu.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Common; 2 | using NanoUI.Nvg; 3 | using System.Numerics; 4 | 5 | namespace NanoUI.Components.Menus 6 | { 7 | /// 8 | /// UIMenuSubmenu. 9 | /// Note: functions/actions are inherited from UIMenu, but theme from UIMenuItem. 10 | /// 11 | public class UIMenuSubmenu : UIMenu 12 | { 13 | /// 14 | public UIMenuSubmenu() 15 | { 16 | 17 | } 18 | 19 | /// 20 | public UIMenuSubmenu(UIWidget parent, string caption) 21 | : base(parent, caption) 22 | { 23 | Border = false; 24 | 25 | // popup 26 | Popup.RelativePosition = PopupPosition.RightTop; 27 | 28 | ThemeType = typeof(UIMenuItem); 29 | } 30 | 31 | #region Properties 32 | 33 | Thickness? _padding; 34 | 35 | /// 36 | public override Thickness Padding 37 | { 38 | get => _padding ?? GetTheme().MenuItem.Padding; 39 | set => _padding = value; 40 | } 41 | 42 | #endregion 43 | 44 | #region Layout 45 | 46 | /// 47 | public override void PerformLayout(NvgContext ctx) 48 | { 49 | Icon = GetTheme().Fonts.IconCaretRight; 50 | 51 | base.PerformLayout(ctx); 52 | 53 | // check if this is really in context menu 54 | var parent = Parent; 55 | 56 | while (parent != null) 57 | { 58 | // we must check for popups first, because their parent is Screen 59 | if (parent is UIPopup popup) 60 | { 61 | var popupButton = popup.GetParentButton(); 62 | 63 | // check nested 64 | if (popupButton is UIMenuSubmenu menuButton && menuButton != null) 65 | { 66 | // change calculation starting with popup button 67 | parent = menuButton; 68 | } 69 | else if (popup is UIContextMenu contextMenu) 70 | { 71 | // we found ultimate parent - set fixed width same as in context menu 72 | FixedSize = new Vector2(contextMenu.FixedSize.X, 0); 73 | break; 74 | } 75 | } 76 | 77 | parent = parent.Parent; 78 | } 79 | 80 | // stretch with parent 81 | if(Parent != null) 82 | Size = new Vector2(Parent.Size.X, Size.Y); 83 | } 84 | 85 | #endregion 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /source/Components/Scrolling/IScrollable.cs: -------------------------------------------------------------------------------- 1 | using System.Numerics; 2 | 3 | namespace NanoUI.Components.Scrolling 4 | { 5 | /// 6 | /// IScrollable. 7 | /// Note: UIWindow & UIScrollpanel could be scrolled & popup does need to if its UIPopupButton is scrolled. 8 | /// 9 | public interface IScrollable 10 | { 11 | /// 12 | /// Scroll offset is negative or Vectro2.Zero 13 | /// 14 | Vector2 ScrollOffset { get; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /source/Components/Simple/UIFileIcon.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Common; 2 | using NanoUI.Components.Files; 3 | using NanoUI.Nvg; 4 | 5 | namespace NanoUI.Components.Simple 6 | { 7 | /// 8 | /// UIFileIcon. 9 | /// Note: supports dynamic theming. If dynamic theming is not needed, 10 | /// use UIIcon instead of UIFileIcon with fixed Icon & IconColor (better performance). 11 | /// 12 | public class UIFileIcon : UIIcon 13 | { 14 | public UIFileIcon(FileFolderInfo fileFolderInfo) 15 | { 16 | FileFolderInfo = fileFolderInfo; 17 | } 18 | 19 | /// 20 | public UIFileIcon(UIWidget parent) 21 | : base(parent) 22 | { 23 | 24 | } 25 | 26 | #region Properties 27 | 28 | /// 29 | /// FileFolderInfo 30 | /// 31 | public FileFolderInfo FileFolderInfo { get; set; } 32 | 33 | #endregion 34 | 35 | #region Drawing 36 | 37 | /// 38 | public override void Draw(NvgContext ctx) 39 | { 40 | // set icon & icon color 41 | (int Icon, Color IconColor) iconSpec = GetTheme().GetFileIcon(FileFolderInfo); 42 | 43 | Icon = iconSpec.Icon; 44 | IconColor = iconSpec.IconColor; 45 | 46 | base.Draw(ctx); 47 | } 48 | 49 | #endregion 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /source/Components/Simple/UIFileIconText.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Common; 2 | using NanoUI.Components.Files; 3 | using NanoUI.Nvg; 4 | 5 | namespace NanoUI.Components.Simple 6 | { 7 | /// 8 | /// UIFileIconText. 9 | /// Note: supports dynamic theming. If dynamic theming is not needed, 10 | /// use UIIcon instead of UIFileIcon with fixed Icon & IconColor (better performance). 11 | /// 12 | public class UIFileIconText : UIIconText 13 | { 14 | public UIFileIconText(FileFolderInfo fileFolderInfo, string displayName) 15 | { 16 | FileFolderInfo = fileFolderInfo; 17 | Text = displayName; 18 | } 19 | 20 | /// 21 | public UIFileIconText(UIWidget parent) 22 | : base(parent) 23 | { 24 | 25 | } 26 | 27 | #region Properties 28 | 29 | /// 30 | /// FileFolderInfo 31 | /// 32 | public FileFolderInfo FileFolderInfo { get; set; } 33 | 34 | #endregion 35 | 36 | #region Drawing 37 | 38 | /// 39 | public override void Draw(NvgContext ctx) 40 | { 41 | // set icon & icon color 42 | (int Icon, Color IconColor) iconSpec = GetTheme().GetFileIcon(FileFolderInfo); 43 | 44 | Icon = iconSpec.Icon; 45 | IconColor = iconSpec.IconColor; 46 | 47 | base.Draw(ctx); 48 | } 49 | 50 | #endregion 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /source/Components/Simple/UIIcon.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Common; 2 | using NanoUI.Nvg; 3 | using System.Numerics; 4 | using System.Text.Json.Serialization; 5 | 6 | namespace NanoUI.Components.Simple 7 | { 8 | /// 9 | /// UIIcon. 10 | /// 11 | public class UIIcon : UIWidget 12 | { 13 | /// 14 | public UIIcon() 15 | { 16 | 17 | } 18 | 19 | public UIIcon(int icon) 20 | { 21 | Icon = icon; 22 | } 23 | 24 | /// 25 | public UIIcon(UIWidget parent) 26 | : base(parent) 27 | { 28 | 29 | } 30 | 31 | #region Properties 32 | 33 | /// 34 | /// Icon. -1 means no icon. 35 | /// 36 | public virtual int Icon { get; set; } 37 | 38 | float? _iconSize; 39 | 40 | /// 41 | /// IconSize 42 | /// 43 | public float IconSize 44 | { 45 | get => _iconSize?? GetTheme().Widget.FontSize; 46 | set => _iconSize = value; 47 | } 48 | 49 | // todo: in base Widget 50 | int? _fontIconsId; 51 | 52 | /// 53 | [JsonIgnore] 54 | public override int FontIconsId 55 | { 56 | get =>_fontIconsId?? GetTheme().Fonts.GetFontId(GetTheme().Fonts.GetDefaultIconType()); 57 | set => _fontIconsId = value; 58 | } 59 | 60 | TextAlignment? _alignment; 61 | 62 | /// 63 | /// Text alignment 64 | /// 65 | public TextAlignment Alignment 66 | { 67 | get => _alignment ?? TextAlignment.Left | TextAlignment.Middle; 68 | set => _alignment = value; 69 | } 70 | 71 | // todo: should this be in Widget? 72 | Color? _iconColor; 73 | 74 | /// 75 | /// Icon color 76 | /// 77 | public virtual Color IconColor 78 | { 79 | get => _iconColor?? GetTheme().Widget.TextColor; 80 | set => _iconColor = value; 81 | } 82 | 83 | #endregion 84 | 85 | #region Drawing 86 | 87 | /// 88 | public override void Draw(NvgContext ctx) 89 | { 90 | if (Icon <= 0) 91 | return; 92 | 93 | ctx.FontSize(IconSize); 94 | ctx.FontFaceId(FontIconsId); 95 | 96 | Vector2 center = Position + Size * 0.5f; 97 | 98 | ctx.FillColor(IconColor); 99 | 100 | ctx.TextAlign(Alignment); 101 | 102 | ctx.Text(Position.X, center.Y, Icon); 103 | } 104 | 105 | #endregion 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /source/Components/Simple/UIImage.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Common; 2 | using NanoUI.Nvg; 3 | using System.Numerics; 4 | 5 | namespace NanoUI.Components.Simple 6 | { 7 | /// 8 | /// UIImage. 9 | /// 10 | public class UIImage : UIWidget 11 | { 12 | // calculated texture size 13 | Vector2 _imgSize; 14 | 15 | public UIImage(int texture) 16 | { 17 | Texture = texture; 18 | } 19 | 20 | /// 21 | public UIImage(UIWidget parent) 22 | : base(parent) 23 | { 24 | 25 | } 26 | 27 | #region Properties 28 | 29 | int _texture; 30 | 31 | /// 32 | /// Texture id. 33 | /// 34 | public int Texture 35 | { 36 | get => _texture; 37 | set 38 | { 39 | _texture = value; 40 | 41 | if(_texture > Globals.INVALID) 42 | { 43 | // calculate image size 44 | NvgContext.Instance.GetTextureSize(Texture, out _imgSize); 45 | } 46 | } 47 | } 48 | 49 | /// 50 | /// Tint color 51 | /// 52 | public Color TintColor { get; set; } 53 | 54 | #endregion 55 | 56 | #region Drawing 57 | 58 | // todo: does this handle case when width != height 59 | 60 | /// 61 | public override void Draw(NvgContext ctx) 62 | { 63 | if (Texture <= Globals.INVALID) 64 | return; 65 | 66 | 67 | // note: Y is calculated dynamically from thumb size & image real size 68 | // todo: these calculations could be in PerformLayout? 69 | // todo2: make confugurable MathF.Min(thumbSize, Size.X)? 70 | float thumbSize = Size.X; 71 | 72 | float iw, ih, ix, iy; 73 | 74 | if (_imgSize.X < _imgSize.Y) 75 | { 76 | iw = thumbSize; 77 | ih = iw * _imgSize.Y / _imgSize.X; 78 | ix = 0; 79 | iy = -(ih - thumbSize) * 0.5f; 80 | } 81 | else 82 | { 83 | ih = thumbSize; 84 | iw = ih * _imgSize.X / _imgSize.Y; 85 | ix = -(iw - thumbSize) * 0.5f; 86 | iy = 0; 87 | } 88 | 89 | // draw texture 90 | Paint imgPaint = Paint.ImagePattern( 91 | Position.X + ix, Position.Y + iy, iw, ih, 0, Texture, TintColor); 92 | 93 | ctx.BeginPath(); 94 | ctx.Rect(Position.X, Position.Y, thumbSize, thumbSize); 95 | ctx.FillPaint(imgPaint); 96 | ctx.Fill(); 97 | } 98 | 99 | #endregion 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /source/Components/Simple/UISeparator.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Common; 2 | using NanoUI.Nvg; 3 | using System.Numerics; 4 | 5 | namespace NanoUI.Components.Simple 6 | { 7 | /// 8 | /// UISeparator. 9 | /// 10 | public class UISeparator : UIWidget 11 | { 12 | public UISeparator(int height) 13 | { 14 | Height = height; 15 | } 16 | 17 | /// 18 | public UISeparator(UIWidget parent) 19 | : base(parent) 20 | { 21 | 22 | } 23 | 24 | #region Properties 25 | 26 | // todo: also orientation vertical & Height => Dimension? 27 | float? _height; 28 | 29 | /// 30 | /// Height 31 | /// 32 | public override float Height 33 | { 34 | get => _height?? 1f; 35 | set => _height = value; 36 | } 37 | 38 | // todo: configurable? 39 | 40 | Color? _color; 41 | 42 | /// 43 | /// Color. If not set makes this dimmer(currently TextColor * 0.7f). 44 | /// 45 | public Color Color 46 | { 47 | get => _color?? GetTheme().Widget.TextColor * 0.7f; 48 | set => _color = value; 49 | } 50 | 51 | /// 52 | /// Padding is set in view panel - when drawing. 53 | /// 54 | internal int Padding { get; set; } 55 | 56 | #endregion 57 | 58 | #region Drawing 59 | 60 | /// 61 | public override void Draw(NvgContext ctx) 62 | { 63 | ctx.BeginPath(); 64 | ctx.StrokeWidth(Height); 65 | 66 | ctx.StrokeColor(Color); 67 | 68 | // todo : use column padding or this Padding? 69 | var center = Position + new Vector2(Padding, Size.Y / 2); 70 | 71 | ctx.MoveTo(center); 72 | ctx.LineTo(center + new Vector2(Size.X - 2 * Padding, 0)); 73 | 74 | ctx.Stroke(); 75 | } 76 | 77 | #endregion 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /source/Components/Simple/UIShortcut.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Common; 2 | using NanoUI.Nvg; 3 | 4 | namespace NanoUI.Components.Simple 5 | { 6 | /// 7 | /// UIShortcut. 8 | /// 9 | public class UIShortcut : UIWidget 10 | { 11 | /// 12 | public UIShortcut() 13 | { 14 | 15 | } 16 | 17 | public UIShortcut(Shortcut shortcut) 18 | : this() 19 | { 20 | Shortcut = shortcut; 21 | } 22 | 23 | /// 24 | public UIShortcut(UIWidget parent) 25 | : base(parent) 26 | { 27 | 28 | } 29 | 30 | #region Properties 31 | 32 | float? _fontSize; 33 | 34 | /// 35 | public override float FontSize 36 | { 37 | get => _fontSize ?? GetTheme().Widget.FontSize; 38 | set => _fontSize = value; 39 | } 40 | 41 | TextAlignment? _alignment; 42 | 43 | /// 44 | /// Text alignment 45 | /// 46 | public TextAlignment Alignment 47 | { 48 | get => _alignment ?? TextAlignment.Left | TextAlignment.Middle; 49 | set => _alignment = value; 50 | } 51 | 52 | Color? _color; 53 | 54 | /// 55 | /// Color 56 | /// 57 | public virtual Color Color 58 | { 59 | get => _color ?? GetTheme().Widget.TextColor; 60 | set => _color = value; 61 | } 62 | 63 | /// 64 | /// Shortcut 65 | /// 66 | public Shortcut? Shortcut { get; set; } 67 | 68 | #endregion 69 | 70 | #region Drawing 71 | 72 | /// 73 | public override void Draw(NvgContext ctx) 74 | { 75 | if (!Shortcut.HasValue) 76 | return; 77 | 78 | ctx.FontSize(FontSize); 79 | ctx.FontFaceId(FontFaceId); 80 | 81 | ctx.TextAlign(Alignment); 82 | ctx.FillColor(Color); 83 | 84 | // note: top left 85 | ctx.Text(Position.X, Position.Y + Size.Y * 0.5f, Shortcut.Value.ToString()); 86 | } 87 | 88 | #endregion 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /source/Components/Simple/UIText.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Common; 2 | using NanoUI.Nvg; 3 | using System.Numerics; 4 | 5 | namespace NanoUI.Components.Simple 6 | { 7 | // todo: remove this - use UIlabel 8 | 9 | /// 10 | /// UIText. 11 | /// 12 | public class UIText : UIWidget 13 | { 14 | /// 15 | public UIText() 16 | { 17 | 18 | } 19 | 20 | /// 21 | public UIText(string text) 22 | : this() 23 | { 24 | Text = text; 25 | } 26 | 27 | public UIText(UIWidget parent) 28 | : base(parent) 29 | { 30 | 31 | } 32 | 33 | #region Properties 34 | 35 | /// 36 | /// Text 37 | /// 38 | public string? Text { get; set; } 39 | 40 | float? _fontSize; 41 | 42 | /// 43 | public override float FontSize 44 | { 45 | get => _fontSize?? GetTheme().Widget.FontSize; 46 | set => _fontSize = value; 47 | } 48 | 49 | float? _charSpacing; 50 | 51 | /// 52 | /// Char spacing is additional spacing (default is 0). 53 | /// 54 | public float CharSpacing 55 | { 56 | get => _charSpacing?? 0; 57 | set => _charSpacing = value; 58 | } 59 | 60 | /// 61 | /// LineHeight is proportional line height (default is 1). 62 | /// 63 | float? _lineHeight; 64 | public float LineHeight 65 | { 66 | get => _lineHeight ?? 1; 67 | set => _lineHeight = value; 68 | } 69 | 70 | TextAlignment? _alignment; 71 | 72 | /// 73 | /// Text alignment 74 | /// 75 | public TextAlignment Alignment 76 | { 77 | get => _alignment?? TextAlignment.Left | TextAlignment.Middle; 78 | set => _alignment = value; 79 | } 80 | 81 | Color? _textColor; 82 | 83 | /// 84 | public override Color TextColor 85 | { 86 | get => _textColor?? GetTheme().Widget.TextColor; 87 | set => _textColor = value; 88 | } 89 | 90 | #endregion 91 | 92 | #region Drawing 93 | 94 | // todo : calculate correct alignment 95 | 96 | /// 97 | public override void Draw(NvgContext ctx) 98 | { 99 | if (string.IsNullOrEmpty(Text)) 100 | return; 101 | 102 | // todo: alignment etc 103 | Vector2 textPos = Position + new Vector2(0, Size.Y * 0.5f); 104 | 105 | ctx.FontSize(FontSize); 106 | ctx.FontFaceId(FontFaceId); 107 | ctx.TextCharSpacing(CharSpacing); 108 | ctx.TextLineHeight(LineHeight); 109 | 110 | // todo: align correctly 111 | ctx.TextAlign(Alignment); 112 | ctx.FillColor(TextColor); 113 | 114 | // draw 115 | ctx.Text(textPos, Text); 116 | } 117 | 118 | #endregion 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /source/Components/UIComboBox.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Common; 2 | using NanoUI.Components.Simple; 3 | using NanoUI.Components.Views; 4 | using NanoUI.Components.Views.Items; 5 | using NanoUI.Nvg; 6 | using System; 7 | 8 | namespace NanoUI.Components 9 | { 10 | /// 11 | /// UIComboBox is just simple combo box with only 1 column (item text). 12 | /// 13 | public class UIComboBox : UIDropDownView 14 | { 15 | // these are used to calculate combo box popup min width 16 | const int ITEM_SAFETY_MARGIN = 18; 17 | 18 | string _longestCaption = string.Empty; 19 | 20 | /// 21 | public UIComboBox(UIWidget parent) 22 | : base(parent) 23 | { 24 | 25 | } 26 | 27 | #region Methods 28 | 29 | /// 30 | /// AddItem 31 | /// 32 | /// Caption 33 | /// Data that is passed when selected 34 | /// 35 | public UIViewItemWidget AddItem(string caption, T eventData) 36 | { 37 | // get the longest caption so we can calculate min width of the display column / popup 38 | if(_longestCaption.Length < caption.Length) 39 | { 40 | _longestCaption = caption; 41 | } 42 | 43 | UIWidget[] cells = 44 | [ 45 | new UIText { Text = caption }, 46 | ]; 47 | 48 | return Add(new RowItem(cells, eventData)); 49 | } 50 | 51 | #endregion 52 | 53 | #region Layout 54 | 55 | /// 56 | public override void PerformLayout(NvgContext ctx) 57 | { 58 | // calculate longest item width 59 | ctx.FontFaceId(FontFaceId); 60 | ctx.FontSize(FontSize); 61 | ctx.TextAlign(TextAlignment.Left | TextAlignment.Top); 62 | var longestItemWidth = ctx.TextBounds(0, 0, _longestCaption, out _); 63 | 64 | // there is only 1 column (combo item text) 65 | // get the max of width / longest item width 66 | Columns = 67 | [ 68 | new ColumnDefinition((int)Math.Max((int)longestItemWidth + 69 | GetTheme().Scrollbars.ScrollbarDimension + ITEM_SAFETY_MARGIN, Size.X)) 70 | ]; 71 | 72 | base.PerformLayout(ctx); 73 | } 74 | 75 | #endregion 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /source/Components/UIEnumDropDown.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace NanoUI.Components 4 | { 5 | /// 6 | /// UIEnumDropDown is just simple enum combo box with only 1 column (enum text). 7 | /// 8 | public class UIEnumDropDown : UIComboBox where T : struct, Enum 9 | { 10 | /// 11 | public UIEnumDropDown(UIWidget parent) 12 | : base(parent) 13 | { 14 | foreach (T value in Enum.GetValues()) 15 | { 16 | AddItem(value.ToString(), value); 17 | } 18 | } 19 | 20 | #region Methods 21 | 22 | /// 23 | public override void SetSelected(T @enum) 24 | { 25 | int index = 0; 26 | 27 | foreach (var item in Enum.GetValues()) 28 | { 29 | if (item.Equals(@enum)) 30 | { 31 | SelectedIndex = index; 32 | break; 33 | } 34 | index++; 35 | } 36 | } 37 | 38 | #endregion 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /source/Components/UIListBox.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Common; 2 | using NanoUI.Components.Simple; 3 | using NanoUI.Components.Views; 4 | using NanoUI.Components.Views.Items; 5 | using NanoUI.Nvg; 6 | 7 | namespace NanoUI.Components 8 | { 9 | /// 10 | /// UIListBox is simple listbox, that displays strings & returns T when item selected. 11 | /// 12 | public class UIListBox : UIListView 13 | { 14 | /// 15 | public UIListBox(UIWidget parent) 16 | : base(parent) 17 | { 18 | 19 | } 20 | 21 | #region Methods 22 | 23 | /// 24 | /// Add item 25 | /// 26 | /// Caption 27 | /// Passed event data, when selected 28 | /// 29 | public UIViewItemWidget AddItem(string caption, T eventData) 30 | { 31 | UIWidget[] cells = 32 | [ 33 | new UIText { Text = caption }, 34 | ]; 35 | 36 | return Add(new RowItem(cells, eventData)); 37 | } 38 | 39 | #endregion 40 | 41 | #region Layout 42 | 43 | /// 44 | public override void PerformLayout(NvgContext ctx) 45 | { 46 | // there is only 1 column (list item text) 47 | ViewPanel.Columns = 48 | [ 49 | new ColumnDefinition((int)Size.X) 50 | ]; 51 | 52 | base.PerformLayout(ctx); 53 | } 54 | 55 | #endregion 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /source/Components/UIPanel.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Nvg; 2 | 3 | namespace NanoUI.Components 4 | { 5 | /// 6 | /// UIPanel is a simple container widget, that only draws background (if spesified) and children. 7 | /// 8 | public class UIPanel : UIWidget 9 | { 10 | /// 11 | public UIPanel(UIWidget parent) 12 | : base(parent) 13 | { 14 | 15 | } 16 | 17 | /// 18 | public override void Draw(NvgContext ctx) 19 | { 20 | // background 21 | DrawBackgroundBrush(ctx); 22 | 23 | // draw children 24 | base.Draw(ctx); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /source/Components/UIProgressbar.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Common; 2 | using NanoUI.Nvg; 3 | using System; 4 | using System.ComponentModel; 5 | 6 | namespace NanoUI.Components 7 | { 8 | // todo : Orientation 9 | // todo : draw progress text value into the center of widget 10 | 11 | /// 12 | /// UIProgressbar. 13 | /// 14 | public class UIProgressbar : UIWidget 15 | { 16 | /// 17 | public UIProgressbar() 18 | { 19 | // set defaults to theme impl - prevents circular reference 20 | ValueColor = default; 21 | } 22 | 23 | /// 24 | public UIProgressbar(UIWidget parent) 25 | : base(parent) 26 | { 27 | ThemeType = typeof(UIProgressbar); 28 | } 29 | 30 | #region Properties 31 | 32 | /// 33 | /// Value 34 | /// 35 | [Browsable(false)] 36 | public virtual float Value { get; set; } 37 | 38 | Color? _valueColor; 39 | 40 | /// 41 | /// Value color 42 | /// 43 | public Color ValueColor 44 | { 45 | get => _valueColor?? GetTheme().Progressbar.ValueColor; 46 | set => _valueColor = value; 47 | } 48 | 49 | #endregion 50 | 51 | #region Drawing 52 | 53 | /// 54 | public override void Draw(NvgContext ctx) 55 | { 56 | // background 57 | DrawBackgroundBrush(ctx); 58 | 59 | // todo : there should not be children but call base.Draw anyway 60 | base.Draw(ctx); 61 | 62 | // Progress 63 | float value = Math.Clamp(Value, 0.0f, 1.0f); 64 | int barPos = (int)Math.Round((Size.X - 2) * value); 65 | 66 | // todo : magical numbers 67 | var cornerRadius = CornerRadius.TopLeft; 68 | 69 | Paint paint = Paint.BoxGradient( 70 | Position.X, Position.Y, 71 | barPos + 1.5f, Size.Y - 1, cornerRadius, 4, // corner radius fixed was 3 72 | ValueColor, ValueColor); 73 | 74 | ctx.BeginPath(); 75 | ctx.RoundedRectVarying( 76 | Position.X + 1, Position.Y + 1, 77 | barPos, Size.Y - 2, CornerRadius); 78 | 79 | ctx.FillPaint(paint); 80 | ctx.Fill(); 81 | 82 | // border 83 | this.DrawBorder(ctx, true); 84 | } 85 | 86 | #endregion 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /source/Components/UISvgWidget.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Nvg; 2 | using System.Numerics; 3 | 4 | namespace NanoUI.Components 5 | { 6 | /// 7 | /// UISvgWidget. 8 | /// 9 | public class UISvgWidget : UIWidget 10 | { 11 | /// 12 | public UISvgWidget() 13 | { 14 | 15 | } 16 | 17 | /// 18 | public UISvgWidget(UIWidget parent) 19 | : base(parent) 20 | { 21 | 22 | } 23 | 24 | #region Properties 25 | 26 | int? svgId; 27 | 28 | /// 29 | /// Svg id 30 | /// 31 | public int? SvgId 32 | { 33 | get => svgId; 34 | set => svgId = value; 35 | } 36 | 37 | /// 38 | /// Shall we scale svg so it fits into widgets area. Default: true; 39 | /// 40 | public bool FitSvg { get; set; } = true; 41 | 42 | #endregion 43 | 44 | #region Drawing 45 | 46 | /// 47 | public override void Draw(NvgContext ctx) 48 | { 49 | // Background 50 | DrawBackgroundBrush(ctx); 51 | 52 | if (svgId != null) 53 | { 54 | ctx.SaveState(); 55 | 56 | // move to widgets parent position 57 | ctx.Translate(Position); 58 | 59 | if(FitSvg && ctx.TryGetSvgSize(svgId.Value, out Vector2 svgSize)) 60 | { 61 | // we must set scale so svg fits in this widget 62 | ctx.Scale(Size / svgSize); 63 | } 64 | 65 | ctx.DrawSvg(svgId.Value); 66 | 67 | ctx.RestoreState(); 68 | } 69 | 70 | base.Draw(ctx); 71 | } 72 | 73 | #endregion 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /source/Components/Views/IViewItem.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace NanoUI.Components.Views 3 | { 4 | // todo: use guid's as id 5 | 6 | /// 7 | /// IViewItem. 8 | /// 9 | public interface IViewItem 10 | { 11 | /// 12 | /// Obligatory id in flat but mabdatory in hierarcial structures. 13 | /// 14 | string? Id { get; } 15 | 16 | /// 17 | /// ParentId is needed to place correctly into hierarcial structure. 18 | /// 19 | string? ParentId { get; } 20 | 21 | /// 22 | /// Widgets 23 | /// 24 | UIWidget[]? Widgets { get; set; } 25 | 26 | /// 27 | /// Event data 28 | /// 29 | T? EventData { get; set; } 30 | 31 | /// 32 | /// If row height is not defined, view item widget uses default row height from theme. 33 | /// 34 | int? RowHeight { get; set; } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /source/Components/Views/Items/FlowItem.cs: -------------------------------------------------------------------------------- 1 | namespace NanoUI.Components.Views.Items 2 | { 3 | // todo: use only 1 cell 4 | // note: items are just sligthly different, but they are in separate classes in order to force use 5 | // right ctor params & with correct view 6 | // todo: struct? 7 | // todo: use guid's as id 8 | 9 | /// 10 | /// FlowItem. 11 | /// 12 | public class FlowItem : IViewItem 13 | { 14 | /// 15 | /// Obligatory id in flat but mandatory in hierarcial structures. 16 | /// 17 | public string? Id { get; } 18 | 19 | /// 20 | /// ParentId is needed to place correctly into hierarcial structure. 21 | /// 22 | public string? ParentId { get; } 23 | 24 | /// 25 | /// Widgets 26 | /// 27 | public UIWidget[]? Widgets { get; set; } 28 | 29 | /// 30 | /// Event data 31 | /// 32 | public T? EventData { get; set; } 33 | 34 | /// 35 | /// If row height is not defined, view item widget uses default row height from theme. 36 | /// 37 | public int? RowHeight { get; set; } 38 | 39 | public FlowItem(T eventData) 40 | { 41 | EventData = eventData; 42 | } 43 | 44 | public FlowItem(UIWidget widget, T eventData) 45 | { 46 | Widgets = [widget]; 47 | EventData = eventData; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /source/Components/Views/Items/RowItem.cs: -------------------------------------------------------------------------------- 1 | namespace NanoUI.Components.Views.Items 2 | { 3 | // note: items are just sligthly different, but they are in separate classes in order to force use 4 | // right ctor params & with correct view 5 | // todo: struct? 6 | // todo: use guid's as id 7 | 8 | /// 9 | /// RowItem. 10 | /// 11 | public class RowItem : IViewItem 12 | { 13 | /// 14 | /// Obligatory id in flat but mandatory in hierarcial structures. 15 | /// 16 | public string? Id { get; } 17 | 18 | /// 19 | /// ParentId is needed to place correctly into hierarcial structure. 20 | /// 21 | public string? ParentId { get; } 22 | 23 | /// 24 | /// Widgets 25 | /// 26 | public UIWidget[]? Widgets { get; set; } 27 | 28 | /// 29 | /// Event data 30 | /// 31 | public T? EventData { get; set; } 32 | 33 | /// 34 | /// If row height is not defined, view item widget uses default row height from theme. 35 | /// 36 | public int? RowHeight { get; set; } 37 | 38 | // todo : check, cells are set in extension 39 | 40 | public RowItem(T eventData) 41 | { 42 | EventData = eventData; 43 | } 44 | 45 | public RowItem(UIWidget[] widgets, T eventData) 46 | { 47 | Widgets = widgets; 48 | EventData = eventData; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /source/Components/Views/Items/TreeItem.cs: -------------------------------------------------------------------------------- 1 | namespace NanoUI.Components.Views.Items 2 | { 3 | // note: items are just sligthly different, but they are in separate classes in order to force use 4 | // right ctor params & with correct view 5 | // todo: struct? 6 | // todo: use guid's as id 7 | 8 | /// 9 | /// TreeItem. 10 | /// 11 | public class TreeItem : IViewItem 12 | { 13 | /// 14 | /// Obligatory id in flat but mandatory in hierarcial structures. 15 | /// 16 | public string? Id { get; internal set; } 17 | 18 | /// 19 | /// ParentId is needed to place correctly into hierarcial structure. 20 | /// 21 | public string? ParentId { get; internal set; } 22 | 23 | /// 24 | /// Widgets 25 | /// 26 | public UIWidget[]? Widgets { get; set; } 27 | 28 | /// 29 | /// Event data 30 | /// 31 | public T? EventData { get; set; } 32 | 33 | /// 34 | /// If row height is not defined, view item widget uses default row height from theme. 35 | /// 36 | public int? RowHeight { get; set; } 37 | 38 | /// 39 | /// Constructor. For hierarcial structures (treeview) - id & parentId are mandatory. 40 | /// 41 | public TreeItem(T eventData, string? id, string? parentId) 42 | { 43 | EventData = eventData; 44 | Id = id; 45 | ParentId = parentId; 46 | } 47 | 48 | /// 49 | /// Constructor. For hierarcial structures (treeview) - id & parentId are mandatory. 50 | /// 51 | public TreeItem(UIWidget[] widgets, T eventData, string? id, string? parentId) 52 | { 53 | Widgets = widgets; 54 | EventData = eventData; 55 | Id = id; 56 | ParentId = parentId; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /source/Components/Views/UIFlowView.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Common; 2 | using NanoUI.Layouts; 3 | using NanoUI.Components.Views.Items; 4 | using System.Numerics; 5 | 6 | namespace NanoUI.Components.Views 7 | { 8 | // todo : Show name under Texture/Icon (truncated/wrapped if not fits to thumbsize) - CUSTOM CELL? 9 | // todo : Show tooltip (description) under Texture/Icon if spesified? 10 | 11 | /// 12 | /// UIFlowView. 13 | /// 14 | public class UIFlowView : UIViewWidget 15 | { 16 | /// 17 | public UIFlowView() 18 | { 19 | // set defaults to theme impl - prevents circular reference 20 | // note: user can set default part size 21 | // PartSize = new Vector2(100, 100); 22 | } 23 | 24 | /// 25 | public UIFlowView(UIWidget parent) 26 | : base(parent) 27 | { 28 | // note: we create view panel in base 29 | 30 | // override default 31 | ViewPanel.ChildrenLayout = new FlowLayout(LayoutAlignment.Minimum) { Spacing = new Vector2(10) }; 32 | ViewPanel.ViewSelectionMode = ViewSelectionMode.Cell; 33 | } 34 | 35 | #region Properties 36 | 37 | // todo: If user changes this, we must update panel widgets Fixed size & panel layout 38 | // todo: we don't store param here, since it produces sync problem, if user changes columns/rowheight directly 39 | Vector2 _partSize; 40 | 41 | /// 42 | /// PartSize is just a helper. 43 | /// 44 | /// Since Columns (ViewColumn[]) is not stored with theme (has owner widget). 45 | public Vector2 PartSize 46 | { 47 | get => _partSize; 48 | set 49 | { 50 | _partSize = value; 51 | 52 | ViewPanel.Columns = [new ColumnDefinition((int)_partSize.X)]; 53 | ViewPanel.RowHeight = (int)_partSize.Y; 54 | 55 | // todo: sync all view panel widgets & perform layout if this is changed at runtime 56 | // (must sync also vscrollpanel) 57 | } 58 | } 59 | 60 | #endregion 61 | 62 | #region Methods 63 | 64 | /// 65 | /// Adds item. 66 | /// 67 | /// FlowItem 68 | /// UIViewItemWidget 69 | public UIViewItemWidget Add(FlowItem flowItem) 70 | { 71 | return new UIViewItemWidget(ViewPanel, flowItem) 72 | { 73 | StretchWidth = true, 74 | FixedSize = PartSize, 75 | CornerRadius = new CornerRadius(0) 76 | }; 77 | } 78 | 79 | #endregion 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /source/Components/Views/UIListView.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Components.Views.Items; 2 | 3 | namespace NanoUI.Components.Views 4 | { 5 | /// 6 | /// UIListView. 7 | /// 8 | public class UIListView : UIViewWidget 9 | { 10 | /// 11 | public UIListView() 12 | { 13 | // set defaults to theme impl - prevents circular reference 14 | } 15 | 16 | /// 17 | public UIListView(UIWidget parent) 18 | :base(parent) 19 | { 20 | // note: we create view panel in base 21 | } 22 | 23 | #region Methods 24 | 25 | /// 26 | /// Adds item. 27 | /// Cells can have their own data OR row can have common data. 28 | /// 29 | /// RowItem 30 | /// widget so you can set common properties. 31 | public UIViewItemWidget Add(RowItem listItem) 32 | { 33 | return new UIViewItemWidget(ViewPanel, listItem) { StretchWidth = true }; 34 | } 35 | 36 | #endregion 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /source/Components/Views/UITableView.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Common; 2 | using NanoUI.Components.Views.Items; 3 | 4 | namespace NanoUI.Components.Views 5 | { 6 | // todo : draw column headers & provide sort by clicked column 7 | 8 | /// 9 | /// UITableView is a generic table view using cells. 10 | /// 11 | public class UITableView : UIViewWidget 12 | { 13 | /// 14 | public UITableView() 15 | { 16 | // set defaults to theme impl - prevents circular reference 17 | } 18 | 19 | /// 20 | public UITableView(UIWidget parent) 21 | :base(parent) 22 | { 23 | // note: we create view panel in base 24 | 25 | // override default 26 | ViewPanel.ViewSelectionMode = ViewSelectionMode.Cell; 27 | } 28 | 29 | #region Methods 30 | 31 | /// 32 | /// Adds item. 33 | /// 34 | /// RowItem 35 | /// UIViewItemWidget 36 | public UIViewItemWidget Add(RowItem viewItem) 37 | { 38 | return new UIViewItemWidget(ViewPanel, viewItem) { StretchWidth = true }; 39 | } 40 | 41 | #endregion 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /source/Components/Views/UIViewItemWidget.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Components.Simple; 2 | using NanoUI.Nvg; 3 | using System.Numerics; 4 | 5 | namespace NanoUI.Components.Views 6 | { 7 | /// 8 | /// UIViewItemWidget. Most of the views / view panels use this as is 9 | /// (exception: UITreeItemWidget). 10 | /// 11 | public class UIViewItemWidget : UIWidget 12 | { 13 | /// 14 | public UIViewItemWidget(UIWidget parent, IViewItem viewRow) 15 | : base(parent) 16 | { 17 | // change/set item cells parent to this 18 | if(viewRow.Widgets != null) 19 | { 20 | foreach (var item in viewRow.Widgets) 21 | { 22 | item.CreateParented(this); 23 | } 24 | } 25 | 26 | 27 | // set params 28 | Name = viewRow.Id?? "No name"; 29 | ParentId = viewRow.ParentId?? ""; 30 | EventData = viewRow.EventData; 31 | 32 | // if row height not set - use default 33 | Size = new Vector2(Size.X, viewRow.RowHeight.HasValue ? viewRow.RowHeight.Value : GetTheme().ViewPanel.RowHeight); 34 | 35 | // todo : should these be in theme? 36 | // note: these are overridden if focused/pointerFocused (uses theme colors) 37 | //BackgroundMode = BackgroundMode.NONE; 38 | Border = false; 39 | } 40 | 41 | #region Properties 42 | 43 | /// 44 | /// StretchWidth determines if we sync width with parent width. 45 | /// Default: false. 46 | /// 47 | /// Only flow & menu view currently doesn't use parent strech. 48 | public bool StretchWidth { get; set; } = false; 49 | 50 | /// 51 | /// ParentId is needed to place correctly into hierarcial structure. 52 | /// 53 | public string ParentId { get; } 54 | 55 | /// 56 | /// EventData. 57 | /// 58 | public T? EventData { get; set; } 59 | 60 | /// 61 | /// IsSeparator. 62 | /// 63 | /// Special handling for separator (do not pointer click/focus etc). 64 | public bool IsSeparator 65 | { 66 | get => Children.Count == 1 && Children[0] is UISeparator; 67 | } 68 | 69 | #endregion 70 | 71 | #region Layout 72 | 73 | /// 74 | public override void PerformLayout(NvgContext ctx) 75 | { 76 | base.PerformLayout(ctx); 77 | 78 | if (Parent != null && StretchWidth) 79 | { 80 | Size = new Vector2(Parent.Size.X, Size.Y); 81 | } 82 | } 83 | 84 | #endregion 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /source/Fonts/Data/Font.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Common; 2 | 3 | namespace NanoUI.Fonts.Data 4 | { 5 | internal struct Font 6 | { 7 | // needed to get glyphs & pass to font manager 8 | public int Id; 9 | // for search purposes 10 | public string Name; 11 | public float Ascender; 12 | public float Descender; 13 | public float LineHeight; 14 | public GlyphBaking FontBaking; 15 | } 16 | } -------------------------------------------------------------------------------- /source/Fonts/Data/FontAtlas.cs: -------------------------------------------------------------------------------- 1 | namespace NanoUI.Fonts.Data 2 | { 3 | internal struct FontAtlas 4 | { 5 | public int Width; 6 | public int Height; 7 | } 8 | } -------------------------------------------------------------------------------- /source/Fonts/Data/FontAtlasNode.cs: -------------------------------------------------------------------------------- 1 | namespace NanoUI.Fonts.Data 2 | { 3 | internal struct FontAtlasNode 4 | { 5 | public int X; 6 | public int Y; 7 | public int Width; 8 | } 9 | } -------------------------------------------------------------------------------- /source/Fonts/Data/FontEnums.cs: -------------------------------------------------------------------------------- 1 | namespace NanoUI.Fonts.Data 2 | { 3 | internal enum CodepointType 4 | { 5 | Space, 6 | Newline, 7 | Char, 8 | CJKChar 9 | } 10 | 11 | internal enum FontGlyphBitmap 12 | { 13 | Optional = 1, 14 | Required = 2 15 | } 16 | } -------------------------------------------------------------------------------- /source/Fonts/Data/FontState.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Common; 2 | 3 | namespace NanoUI.Fonts.Data 4 | { 5 | // note: we can't use NvgState, since there is scaling applied in GetFontScale 6 | internal struct FontState 7 | { 8 | // this is just needed for glyphs 9 | public int FontId; 10 | public TextAlignment TextAlign; 11 | public float FontSize; 12 | public float CharSpacing; 13 | // this is a scale to convert font file's internal units to pixels 14 | public float PixelScale; 15 | public float Blur; 16 | public float Dilate; 17 | // there is different logic in some FontStash functions based on baking method 18 | public GlyphBaking GlyphBaking; 19 | 20 | // glyph shapes 21 | public Color? TextShapeOutline; 22 | public float TextShapeOutlineWidth; 23 | public Paint? TextShapeFill; 24 | } 25 | } -------------------------------------------------------------------------------- /source/Fonts/Data/FontTextIter.cs: -------------------------------------------------------------------------------- 1 | namespace NanoUI.Fonts.Data 2 | { 3 | // note: we use same iterator whole time, so we must reset values 4 | internal struct FontTextIter 5 | { 6 | public float X, Y, NextX, NextY; 7 | public uint Codepoint; 8 | public int PrevGlyphIndex; 9 | 10 | // current pos (char) in string 11 | public int CurrentPos; 12 | // next char pos 13 | public int NextPos; 14 | 15 | public FontGlyphBitmap BitmapOption; 16 | 17 | public void Reset(float x, float y, FontGlyphBitmap bitmapOption) 18 | { 19 | X = NextX = x; 20 | Y = NextY = y; 21 | CurrentPos = NextPos = 0; 22 | Codepoint = 0; 23 | PrevGlyphIndex = -1; 24 | BitmapOption = bitmapOption; 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /source/Fonts/Data/Glyph.cs: -------------------------------------------------------------------------------- 1 | namespace NanoUI.Fonts.Data 2 | { 3 | internal struct Glyph 4 | { 5 | public uint Codepoint; 6 | // this is glyphIndex in Font 7 | public int FontGlyphIndex; 8 | // this is the font size, when glyph was originally created 9 | // note: if we use same glyph with all font sizes, we must calculate scale/advanceX based on this 10 | public float FontSize; 11 | public int X0, Y0, X1, Y1; // min, max 12 | public int OffX, OffY; 13 | public float AdvX; 14 | public int Blur; 15 | public int Dilate; 16 | } 17 | } -------------------------------------------------------------------------------- /source/Fonts/Data/GlyphQuad.cs: -------------------------------------------------------------------------------- 1 | namespace NanoUI.Fonts.Data 2 | { 3 | // this is "area" that glyph occupies in atlas texture 4 | // - x0, y0, s0, t0 = MIN pos & uv 5 | // - x1, y1, s1, t1 = MAX pos & uv 6 | internal struct GlyphQuad 7 | { 8 | public float x0, y0, s0, t0; 9 | public float x1, y1, s1, t1; 10 | } 11 | } -------------------------------------------------------------------------------- /source/Fonts/SafeTrueType/CharStringContext.cs: -------------------------------------------------------------------------------- 1 | using static SafeStbTrueTypeSharp.Common; 2 | 3 | namespace SafeStbTrueTypeSharp 4 | { 5 | // fixed nullable warnings 6 | internal class CharStringContext 7 | { 8 | public int bounds; 9 | public float first_x; 10 | public float first_y; 11 | public int max_x; 12 | public int max_y; 13 | public int min_x; 14 | public int min_y; 15 | public int num_vertices; 16 | public stbtt_vertex[]? pvertices; // fixed 17 | public int started; 18 | public float x; 19 | public float y; 20 | 21 | public void stbtt__track_vertex(int x, int y) 22 | { 23 | if (x > max_x || started == 0) 24 | max_x = x; 25 | if (y > max_y || started == 0) 26 | max_y = y; 27 | if (x < min_x || started == 0) 28 | min_x = x; 29 | if (y < min_y || started == 0) 30 | min_y = y; 31 | started = 1; 32 | } 33 | 34 | public void stbtt__csctx_v(byte type, int x, int y, int cx, int cy, int cx1, int cy1) 35 | { 36 | if (bounds != 0) 37 | { 38 | stbtt__track_vertex(x, y); 39 | if (type == STBTT_vcubic) 40 | { 41 | stbtt__track_vertex(cx, cy); 42 | stbtt__track_vertex(cx1, cy1); 43 | } 44 | } 45 | else 46 | { 47 | var v = new stbtt_vertex(); 48 | stbtt_setvertex(ref v, type, x, y, cx, cy); 49 | pvertices![num_vertices] = v; // fixed 50 | pvertices[num_vertices].cx1 = (short)cx1; 51 | pvertices[num_vertices].cy1 = (short)cy1; 52 | } 53 | 54 | num_vertices++; 55 | } 56 | 57 | public void stbtt__csctx_close_shape() 58 | { 59 | if (first_x != x || first_y != y) 60 | stbtt__csctx_v(STBTT_vline, (int)first_x, (int)first_y, 0, 0, 0, 0); 61 | } 62 | 63 | public void stbtt__csctx_rmove_to(float dx, float dy) 64 | { 65 | stbtt__csctx_close_shape(); 66 | first_x = x = x + dx; 67 | first_y = y = y + dy; 68 | stbtt__csctx_v(STBTT_vmove, (int)x, (int)y, 0, 0, 0, 0); 69 | } 70 | 71 | public void stbtt__csctx_rline_to(float dx, float dy) 72 | { 73 | x += dx; 74 | y += dy; 75 | stbtt__csctx_v(STBTT_vline, (int)x, (int)y, 0, 0, 0, 0); 76 | } 77 | 78 | public void stbtt__csctx_rccurve_to(float dx1, float dy1, float dx2, float dy2, 79 | float dx3, float dy3) 80 | { 81 | var cx1 = x + dx1; 82 | var cy1 = y + dy1; 83 | var cx2 = cx1 + dx2; 84 | var cy2 = cy1 + dy2; 85 | x = cx2 + dx3; 86 | y = cy2 + dy3; 87 | stbtt__csctx_v(STBTT_vcubic, (int)x, (int)y, (int)cx1, (int)cy1, (int)cx2, (int)cy2); 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /source/Fonts/TrueType/StbTrueType.CharString.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace StbTrueTypeSharp 4 | { 5 | unsafe partial class StbTrueType 6 | { 7 | public static void stbtt__csctx_close_shape(stbtt__csctx* ctx) 8 | { 9 | if (ctx->first_x != ctx->x || ctx->first_y != ctx->y) 10 | stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->first_x, (int)ctx->first_y, 0, 0, 0, 0); 11 | } 12 | 13 | public static void stbtt__csctx_rccurve_to(stbtt__csctx* ctx, float dx1, float dy1, float dx2, float dy2, 14 | float dx3, float dy3) 15 | { 16 | var cx1 = ctx->x + dx1; 17 | var cy1 = ctx->y + dy1; 18 | var cx2 = cx1 + dx2; 19 | var cy2 = cy1 + dy2; 20 | ctx->x = cx2 + dx3; 21 | ctx->y = cy2 + dy3; 22 | stbtt__csctx_v(ctx, STBTT_vcubic, (int)ctx->x, (int)ctx->y, (int)cx1, (int)cy1, (int)cx2, (int)cy2); 23 | } 24 | 25 | public static void stbtt__csctx_rline_to(stbtt__csctx* ctx, float dx, float dy) 26 | { 27 | ctx->x += dx; 28 | ctx->y += dy; 29 | stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0); 30 | } 31 | 32 | public static void stbtt__csctx_rmove_to(stbtt__csctx* ctx, float dx, float dy) 33 | { 34 | stbtt__csctx_close_shape(ctx); 35 | ctx->first_x = ctx->x = ctx->x + dx; 36 | ctx->first_y = ctx->y = ctx->y + dy; 37 | stbtt__csctx_v(ctx, STBTT_vmove, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0); 38 | } 39 | 40 | public static void stbtt__csctx_v(stbtt__csctx* c, byte type, int x, int y, int cx, int cy, int cx1, int cy1) 41 | { 42 | if (c->bounds != 0) 43 | { 44 | stbtt__track_vertex(c, x, y); 45 | if (type == STBTT_vcubic) 46 | { 47 | stbtt__track_vertex(c, cx, cy); 48 | stbtt__track_vertex(c, cx1, cy1); 49 | } 50 | } 51 | else 52 | { 53 | stbtt_setvertex(&c->pvertices[c->num_vertices], type, x, y, cx, cy); 54 | c->pvertices[c->num_vertices].cx1 = (short)cx1; 55 | c->pvertices[c->num_vertices].cy1 = (short)cy1; 56 | } 57 | 58 | c->num_vertices++; 59 | } 60 | 61 | public static void stbtt__track_vertex(stbtt__csctx* c, int x, int y) 62 | { 63 | if (x > c->max_x || c->started == 0) 64 | c->max_x = x; 65 | if (y > c->max_y || c->started == 0) 66 | c->max_y = y; 67 | if (x < c->min_x || c->started == 0) 68 | c->min_x = x; 69 | if (y < c->min_y || c->started == 0) 70 | c->min_y = y; 71 | c->started = 1; 72 | } 73 | 74 | [StructLayout(LayoutKind.Sequential)] 75 | public struct stbtt__csctx 76 | { 77 | public int bounds; 78 | public int started; 79 | public float first_x; 80 | public float first_y; 81 | public float x; 82 | public float y; 83 | public int min_x; 84 | public int max_x; 85 | public int min_y; 86 | public int max_y; 87 | public stbtt_vertex* pvertices; 88 | public int num_vertices; 89 | } 90 | } 91 | } -------------------------------------------------------------------------------- /source/Fonts/TrueType/StbTrueType.Heap.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace StbTrueTypeSharp 4 | { 5 | unsafe partial class StbTrueType 6 | { 7 | public static void* stbtt__hheap_alloc(stbtt__hheap* hh, ulong size, void* userdata) 8 | { 9 | if (hh->first_free != null) 10 | { 11 | var p = hh->first_free; 12 | hh->first_free = *(void**)p; 13 | return p; 14 | } 15 | 16 | if (hh->num_remaining_in_head_chunk == 0) 17 | { 18 | var count = size < 32 ? 2000 : size < 128 ? 800 : 100; 19 | var c = (stbtt__hheap_chunk*)CRuntime.malloc((ulong)sizeof(stbtt__hheap_chunk) + 20 | size * (ulong)count); 21 | if (c == null) 22 | return null; 23 | c->next = hh->head; 24 | hh->head = c; 25 | hh->num_remaining_in_head_chunk = count; 26 | } 27 | 28 | --hh->num_remaining_in_head_chunk; 29 | return (sbyte*)hh->head + sizeof(stbtt__hheap_chunk) + size * (ulong)hh->num_remaining_in_head_chunk; 30 | } 31 | 32 | public static void stbtt__hheap_cleanup(stbtt__hheap* hh, void* userdata) 33 | { 34 | var c = hh->head; 35 | while (c != null) 36 | { 37 | var n = c->next; 38 | CRuntime.free(c); 39 | c = n; 40 | } 41 | } 42 | 43 | public static void stbtt__hheap_free(stbtt__hheap* hh, void* p) 44 | { 45 | *(void**)p = hh->first_free; 46 | hh->first_free = p; 47 | } 48 | 49 | public static stbtt__active_edge* stbtt__new_active(stbtt__hheap* hh, stbtt__edge* e, int off_x, 50 | float start_point, void* userdata) 51 | { 52 | var z = (stbtt__active_edge*)stbtt__hheap_alloc(hh, (ulong)sizeof(stbtt__active_edge), userdata); 53 | var dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); 54 | if (z == null) 55 | return z; 56 | z->fdx = dxdy; 57 | z->fdy = dxdy != 0.0f ? 1.0f / dxdy : 0.0f; 58 | z->fx = e->x0 + dxdy * (start_point - e->y0); 59 | z->fx -= off_x; 60 | z->direction = e->invert != 0 ? 1.0f : -1.0f; 61 | z->sy = e->y0; 62 | z->ey = e->y1; 63 | z->next = null; 64 | return z; 65 | } 66 | 67 | [StructLayout(LayoutKind.Sequential)] 68 | public struct stbtt__hheap 69 | { 70 | public stbtt__hheap_chunk* head; 71 | public void* first_free; 72 | public int num_remaining_in_head_chunk; 73 | } 74 | 75 | [StructLayout(LayoutKind.Sequential)] 76 | public struct stbtt__hheap_chunk 77 | { 78 | public stbtt__hheap_chunk* next; 79 | } 80 | } 81 | } -------------------------------------------------------------------------------- /source/Fonts/TrueType/StbTrueType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace StbTrueTypeSharp 5 | { 6 | internal static unsafe partial class StbTrueType 7 | { 8 | internal static bool usedOldRasterizer = false; 9 | 10 | public class stbtt_fontinfo : IDisposable 11 | { 12 | public stbtt__buf cff; 13 | public stbtt__buf charstrings; 14 | public byte* data = null; 15 | public stbtt__buf fdselect; 16 | public stbtt__buf fontdicts; 17 | public int fontstart; 18 | public int glyf; 19 | public int gpos; 20 | public stbtt__buf gsubrs; 21 | public int head; 22 | public int hhea; 23 | public int hmtx; 24 | public int index_map; 25 | public int indexToLocFormat; 26 | public bool isDataCopy; 27 | public int kern; 28 | public int loca; 29 | public int numGlyphs; 30 | public stbtt__buf subrs; 31 | public int svg; 32 | public void* userdata; 33 | public bool useOldRasterizer = false; 34 | 35 | public void Dispose() 36 | { 37 | Dispose(true); 38 | } 39 | 40 | ~stbtt_fontinfo() 41 | { 42 | Dispose(false); 43 | } 44 | 45 | protected virtual void Dispose(bool disposing) 46 | { 47 | if (isDataCopy && data != null) 48 | { 49 | CRuntime.free(data); 50 | data = null; 51 | } 52 | } 53 | } 54 | 55 | public static uint stbtt__find_table(byte* data, uint fontstart, string tag) 56 | { 57 | int num_tables = ttUSHORT(data + fontstart + 4); 58 | var tabledir = fontstart + 12; 59 | int i; 60 | for (i = 0; i < num_tables; ++i) 61 | { 62 | var loc = (uint)(tabledir + 16 * i); 63 | if ((data + loc + 0)[0] == tag[0] && (data + loc + 0)[1] == tag[1] && 64 | (data + loc + 0)[2] == tag[2] && (data + loc + 0)[3] == tag[3]) 65 | return ttULONG(data + loc + 8); 66 | } 67 | 68 | return 0; 69 | } 70 | 71 | public static bool stbtt_BakeFontBitmap(byte[] ttf, int offset, float pixel_height, byte[] pixels, int pw, 72 | int ph, 73 | int first_char, int num_chars, stbtt_bakedchar[] chardata) 74 | { 75 | fixed (byte* ttfPtr = ttf) 76 | { 77 | fixed (byte* pixelsPtr = pixels) 78 | { 79 | fixed (stbtt_bakedchar* chardataPtr = chardata) 80 | { 81 | var result = stbtt_BakeFontBitmap(ttfPtr, offset, pixel_height, pixelsPtr, pw, ph, first_char, 82 | num_chars, 83 | chardataPtr); 84 | 85 | return result != 0; 86 | } 87 | } 88 | } 89 | } 90 | 91 | /// 92 | /// Creates and initializes a font from ttf/otf/ttc data 93 | /// 94 | /// 95 | /// 96 | /// null if the data was invalid 97 | public static stbtt_fontinfo? CreateFont(byte[] data, int offset) 98 | { 99 | var dataCopy = (byte*)CRuntime.malloc(data.Length); 100 | Marshal.Copy(data, 0, new IntPtr(dataCopy), data.Length); 101 | 102 | var info = new stbtt_fontinfo 103 | { 104 | isDataCopy = true 105 | }; 106 | 107 | if (stbtt_InitFont_internal(info, dataCopy, offset) == 0) 108 | { 109 | info.Dispose(); 110 | return null; 111 | } 112 | 113 | return info; 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /source/INvgRenderer.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Common; 2 | using System; 3 | using System.Numerics; 4 | 5 | namespace NanoUI 6 | { 7 | /// 8 | /// NanoUI knows nothing about your graphics system. 9 | /// So you must have INvgRenderer implementation, that handles rendering to your 10 | /// graphics system. 11 | /// Note: You must send this implementation to NanoUI, when you init NvgContext. 12 | /// 13 | public interface INvgRenderer 14 | { 15 | /// 16 | /// Create a texture from the specified path with texture options (flags). 17 | /// 18 | /// Path 19 | /// Texture flags 20 | /// Texture id 21 | /// 22 | /// Path can be any kind of identification you like, since NanoUI passes it as-is. 23 | /// 24 | int CreateTexture(string path, TextureFlags textureFlags = 0); 25 | 26 | /// 27 | /// Create a texture from the texture description. 28 | /// 29 | /// Texture description 30 | /// Texture id 31 | int CreateTexture(TextureDesc description); 32 | 33 | /// 34 | /// Update a texture with data. 35 | /// 36 | /// Texture id 37 | /// Data 38 | /// Success 39 | /// Data is in 8-bit format when updating font texture. 40 | bool UpdateTexture(int texture, ReadOnlySpan data); 41 | 42 | /// 43 | /// Resize a texture according to texture description. 44 | /// 45 | /// Texture id 46 | /// Texture description 47 | /// Success 48 | /// 49 | /// You can copy data from the old texture to the new, but NanoUI is 50 | /// not demanding it when font texture is resized 51 | /// (NanoUI calls UpdateTexture command with all the new data after this). 52 | /// 53 | bool ResizeTexture(int texture, TextureDesc description); 54 | 55 | /// 56 | /// Delete a texture 57 | /// 58 | /// Texture id 59 | /// Success 60 | bool DeleteTexture(int texture); 61 | 62 | /// 63 | /// Get a texture size (width, height) 64 | /// 65 | /// Texture id 66 | /// Texture size 67 | /// Success 68 | bool GetTextureSize(int texture, out Vector2 size); 69 | 70 | /// 71 | /// Triggerred from NvgContext when EndFrame called. 72 | /// DrawCache is cleared after rendering is done. 73 | /// 74 | void Render(); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /source/Layouts/Layout.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Components; 2 | using NanoUI.Nvg; 3 | using System.Numerics; 4 | 5 | namespace NanoUI.Layouts 6 | { 7 | /// 8 | /// Layout is an abstract layout class, that all layout implementations should extend. 9 | /// 10 | public abstract class Layout 11 | { 12 | /// 13 | /// Spacing between widgets. 14 | /// 15 | /// Layouts my use spacing only in 1 dimension. 16 | public Vector2 Spacing { get; set; } 17 | 18 | /// 19 | /// Calculates parent widget's children preferred size with margins. 20 | /// 21 | /// NvgContext 22 | /// Parent 23 | /// Size 24 | public abstract Vector2 PreferredSize(NvgContext ctx, UIWidget parent); 25 | 26 | /// 27 | /// Sets widget's children positions and sizes & calls childs to perform layout, 28 | /// where you can tweak calculated values. 29 | /// 30 | /// NvgContext 31 | /// Parent 32 | /// Calls parent widget's GetLayoutArea method in order to get offset/topLeft & children area size. 33 | public abstract void PerformLayout(NvgContext ctx, UIWidget parent); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /source/NanoUI.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0 5 | Small, extendable and quite feature-rich C# UI and drawing library with no external dependencies, native libraries and no extra assets 6 | disable 7 | enable 8 | True 9 | README.md 10 | true 11 | 12 | 13 | 14 | 15 | 1.0.10 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /source/Nvg/Data/NvgBounds.cs: -------------------------------------------------------------------------------- 1 | using System.Numerics; 2 | 3 | namespace NanoUI.Nvg.Data 4 | { 5 | internal struct NvgBounds 6 | { 7 | public Vector2 Min; 8 | public Vector2 Max; 9 | 10 | public float X 11 | { 12 | get => Min.X; 13 | set => Min.X = value; 14 | } 15 | 16 | public float Y 17 | { 18 | get => Min.Y; 19 | set => Min.Y = value; 20 | } 21 | 22 | public float X2 23 | { 24 | get => Max.X; 25 | set => Max.X = value; 26 | } 27 | 28 | public float Y2 29 | { 30 | get => Max.Y; 31 | set => Max.Y = value; 32 | } 33 | 34 | public NvgBounds(Vector2 min, Vector2 max) 35 | { 36 | Min = min; 37 | Max = max; 38 | } 39 | 40 | public void Reset() 41 | { 42 | Min = new Vector2(1e6f); 43 | Max = new Vector2(-1e6f); 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /source/Nvg/Data/NvgParams.cs: -------------------------------------------------------------------------------- 1 | namespace NanoUI.Nvg.Data 2 | { 3 | internal struct NvgParams 4 | { 5 | public float TessTol { get; private set; } 6 | public float DistTol { get; private set; } 7 | public float FringeWidth { get; private set; } 8 | public float DevicePxRatio { get; private set; } 9 | 10 | // nvg__setDevicePixelRatio 11 | public void SetDevicePixelRatio(float pixelRatio) 12 | { 13 | TessTol = 0.25f / pixelRatio; 14 | DistTol = 0.01f / pixelRatio; 15 | FringeWidth = 1.0f / pixelRatio; 16 | DevicePxRatio = pixelRatio; 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /source/Nvg/Data/NvgPath.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Common; 2 | 3 | namespace NanoUI.Nvg.Data 4 | { 5 | internal struct NvgPath 6 | { 7 | public bool Closed; 8 | public uint BevelCount; 9 | public bool Convex; 10 | public Winding Winding; 11 | public NvgBounds Bounds; 12 | 13 | // structs params 14 | public int PointOffset; 15 | public int PointCount; 16 | public int FillCount; 17 | public int FillOffset; 18 | public int StrokeCount; 19 | public int StrokeOffset; 20 | 21 | // when adding path we must reset values 22 | public void Reset() 23 | { 24 | Closed = default; 25 | BevelCount = default; 26 | Convex = default; 27 | // default winding marks solid fill 28 | Winding = Winding.CounterClockwise; 29 | 30 | Bounds.Reset(); 31 | 32 | // structs params 33 | PointOffset = default; 34 | PointCount = default; 35 | FillCount = default; 36 | FillOffset = default; 37 | StrokeCount = default; 38 | StrokeOffset = default; 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /source/Nvg/Data/NvgPathCommand.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Common; 2 | using System.Numerics; 3 | 4 | namespace NanoUI.Nvg.Data 5 | { 6 | internal struct NvgPathCommand 7 | { 8 | public NvgPathCommandType CommandType; 9 | public Vector2 P0; 10 | public Vector2 P1; 11 | public Vector2 P2; 12 | 13 | public float TessTol; 14 | public Winding Winding; 15 | } 16 | } -------------------------------------------------------------------------------- /source/Nvg/Data/NvgPathCommandType.cs: -------------------------------------------------------------------------------- 1 | namespace NanoUI.Nvg.Data 2 | { 3 | internal enum NvgPathCommandType 4 | { 5 | MoveTo, 6 | LineTo, 7 | BezierTo, 8 | Close, 9 | Winding, 10 | } 11 | } -------------------------------------------------------------------------------- /source/Nvg/Data/NvgPoint.cs: -------------------------------------------------------------------------------- 1 | using System.Numerics; 2 | 3 | namespace NanoUI.Nvg.Data 4 | { 5 | internal struct NvgPoint 6 | { 7 | public Vector2 Position; 8 | public Vector2 Delta; 9 | public float Length; 10 | public Vector2 DeltaM; 11 | public NvgPointFlags Flags; 12 | 13 | public void Reset() 14 | { 15 | Position = Delta = DeltaM = Vector2.Zero; 16 | Length = 0; 17 | Flags = 0;// default; 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /source/Nvg/Data/NvgPointFlags.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace NanoUI.Nvg.Data 4 | { 5 | [Flags] 6 | internal enum NvgPointFlags 7 | { 8 | Corner = 1 << 0, 9 | Left = 1 << 1, 10 | Bevel = 1 << 2, 11 | InnerBevel = 1 << 3 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /source/Nvg/Data/NvgScissor.cs: -------------------------------------------------------------------------------- 1 | using System.Numerics; 2 | 3 | namespace NanoUI.Nvg.Data 4 | { 5 | internal struct NvgScissor 6 | { 7 | public Matrix3x2 Transform; 8 | public Vector2 Extent; 9 | 10 | public void Reset() 11 | { 12 | // todo: identity? 13 | Transform = default; 14 | Extent = new Vector2(-1.0f); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /source/Nvg/Data/NvgState.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Common; 2 | using System.Numerics; 3 | 4 | namespace NanoUI.Nvg.Data 5 | { 6 | // todo: should some values be nullable? 7 | internal struct NvgState 8 | { 9 | public Paint Fill; 10 | public Paint Stroke; 11 | public float StrokeWidth; 12 | public float MiterLimit; 13 | public LineCap LineJoin; 14 | public LineCap LineCap; 15 | public float Alpha; 16 | public Matrix3x2 Transform; 17 | public NvgScissor Scissor; 18 | public float FontSize; 19 | // note: this is additional spacing (default is 0) 20 | public float TextCharSpacing; 21 | // note: this is proportional line height (default is 1) 22 | public float TextLineHeight; 23 | public TextAlignment TextAlign; 24 | public int FontId; 25 | public int TextBlur; 26 | public int TextDilate; 27 | 28 | // glyph shapes 29 | public Color? TextShapeOutline; 30 | public float TextShapeOutlineWidth; 31 | public Paint? TextShapeFill; 32 | 33 | public NvgState() 34 | { 35 | Reset(); 36 | } 37 | 38 | public void Reset() 39 | { 40 | // init defaults 41 | Fill.Reset(Color.White); 42 | Stroke.Reset(Color.Black); 43 | StrokeWidth = 1.0f; 44 | MiterLimit = 10.0f; 45 | LineJoin = LineCap.Miter; 46 | LineCap = LineCap.Butt; 47 | Alpha = 1.0f; 48 | Transform = Matrix3x2.Identity; 49 | Scissor.Reset(); 50 | FontSize = 16.0f; 51 | TextCharSpacing = 0.0f; 52 | TextLineHeight = 1.0f; 53 | TextAlign = TextAlignment.Left | TextAlignment.Baseline; 54 | FontId = 0; 55 | TextBlur = 0; 56 | TextDilate = 0; 57 | // glyph shapes 58 | TextShapeOutline = null; 59 | TextShapeOutlineWidth = 1; 60 | TextShapeFill = null; 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /source/README.md: -------------------------------------------------------------------------------- 1 | Small, extendable and quite feature-rich C# UI & drawing library with no external dependencies, native libraries and no extra assets. 2 | 3 | More information and changelog in the source repository. 4 | -------------------------------------------------------------------------------- /source/Rendering/Data/DrawCall.cs: -------------------------------------------------------------------------------- 1 | namespace NanoUI.Rendering.Data 2 | { 3 | // this is internal draw call info that is used to create draw command(s) for the user 4 | internal struct DrawCall 5 | { 6 | public DrawCallType Type; 7 | 8 | public int Uniform1; 9 | public int Uniform2; 10 | 11 | public int VertexOffset; 12 | public int VertexCount; 13 | 14 | public int FillStrokeCount; 15 | public int FillStrokeOffset; 16 | 17 | public int Texture; 18 | } 19 | } -------------------------------------------------------------------------------- /source/Rendering/Data/DrawCommand.cs: -------------------------------------------------------------------------------- 1 | namespace NanoUI.Rendering.Data 2 | { 3 | /// 4 | /// Draw command to be used when rendering. Renderer should loop all draw commands & 5 | /// prepare graphics engine before drawing. 6 | /// 7 | public struct DrawCommand 8 | { 9 | /// 10 | /// DrawCommandType to select correct pipeline settings. 11 | /// 12 | public DrawCommandType DrawCommandType; 13 | 14 | /// 15 | /// DrawCallType can be used in rendering, if some special operation is needed (optional). 16 | /// 17 | public DrawCallType DrawCallType; 18 | 19 | /// 20 | /// Uniform index. 21 | /// 22 | public int UniformOffset; 23 | 24 | /// 25 | /// Texture id. 26 | /// 27 | public int Texture; 28 | 29 | /// 30 | /// Indices start index. 31 | /// 32 | public int IndexOffset; 33 | 34 | /// 35 | /// Indices count. 36 | /// 37 | public int IndexCount; 38 | 39 | /// 40 | /// Vertices start index. 41 | /// 42 | public int VertexOffset; 43 | 44 | /// 45 | /// Vertices count. 46 | /// 47 | /// 48 | /// You should not use VertexCount when drawing (use indexed drawing), 49 | /// since it will produce unspecified results. 50 | /// 51 | public int VertexCount; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /source/Rendering/Data/FillStrokeInfo.cs: -------------------------------------------------------------------------------- 1 | namespace NanoUI.Rendering.Data 2 | { 3 | internal struct FillStrokeInfo 4 | { 5 | public int FillOffset; 6 | public int FillCount; 7 | 8 | public int StrokeOffset; 9 | public int StrokeCount; 10 | } 11 | } -------------------------------------------------------------------------------- /source/Rendering/Data/FragmentUniform.cs: -------------------------------------------------------------------------------- 1 | using System.Numerics; 2 | 3 | namespace NanoUI.Rendering.Data 4 | { 5 | /// 6 | /// Uniform used in fragment/pixel shader. 7 | /// Note: You shouldn't normally worry about this, since NanoUI sets values and 8 | /// your fragment/pixel shader uses them. 9 | /// 10 | public struct FragmentUniform 11 | { 12 | /// 13 | /// Scissor matrix. 14 | /// 15 | public Matrix4x4 ScissorMat; 16 | 17 | /// 18 | /// Paint matrix. 19 | /// 20 | public Matrix4x4 PaintMat; 21 | 22 | /// 23 | /// Inner color. 24 | /// 25 | public Vector4 InnerCol; 26 | 27 | /// 28 | /// Outer color. 29 | /// 30 | public Vector4 OuterCol; 31 | 32 | /// 33 | /// Scissor extent. 34 | /// 35 | public Vector2 ScissorExt; 36 | 37 | /// 38 | /// Scissor scale. 39 | /// 40 | public Vector2 ScissorScale; 41 | 42 | /// 43 | /// Extent. 44 | /// 45 | public Vector2 Extent; 46 | 47 | /// 48 | /// Radius. 49 | /// 50 | public float Radius; 51 | 52 | /// 53 | /// Feather. 54 | /// 55 | public float Feather; 56 | 57 | /// 58 | /// Action type. 59 | /// 60 | /// ActionType is actually int, but some HLSL shaders don't like ints. 61 | public float ActionType; 62 | 63 | /// 64 | /// Font size. 65 | /// 66 | public float FontSize; 67 | 68 | /// 69 | /// Reserved for future usage. Needed to set correct alignment. 70 | /// 71 | private float Unused1; 72 | 73 | /// 74 | /// Reserved for future usage. Needed to set correct alignment. 75 | /// 76 | private float Unused2; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /source/Rendering/Data/Vertex.cs: -------------------------------------------------------------------------------- 1 | using System.Numerics; 2 | 3 | namespace NanoUI.Rendering.Data 4 | { 5 | /// 6 | /// Vertex to store into VertexBuffer. 7 | /// 8 | public struct Vertex 9 | { 10 | /// 11 | /// Size in bytes. 12 | /// 13 | public const byte SizeInBytes = 16; 14 | 15 | /// 16 | /// Position. 17 | /// 18 | public Vector2 Position; 19 | 20 | /// 21 | /// Texture coordinate. 22 | /// 23 | public Vector2 UV; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /source/Rendering/DrawEnums.cs: -------------------------------------------------------------------------------- 1 | namespace NanoUI.Rendering 2 | { 3 | /// 4 | /// (Optional) DrawCallType can be used in rendering, 5 | /// if some special operation is needed. 6 | /// 7 | public enum DrawCallType 8 | { 9 | Fill, 10 | ConvexFill, 11 | Stroke, 12 | Text 13 | } 14 | 15 | /// 16 | /// DrawCommandType should be used in rendering, 17 | /// when setting correct pipeline settings. 18 | /// 19 | public enum DrawCommandType 20 | { 21 | // TriangleFan --> TriangleList - indexed 22 | FillStencil, 23 | 24 | // TriangleStrip --> TriangleList - indexed 25 | Fill, 26 | 27 | // TriangleFan/TriangleStrip/TriangleList --> TriangleList - indexed 28 | // used by ConvexFill, Stroke, Text 29 | Triangles 30 | } 31 | 32 | /// 33 | /// DrawActionType is used in fragment/pixel shader to determine 34 | /// the correct action. 35 | /// 36 | public enum DrawActionType : int 37 | { 38 | FillGradient = 0, 39 | FillImage = 1, 40 | FillStencil = 2, 41 | Text = 3, 42 | // this allows to use normal & additional text effects in shader (outline, blur etc) 43 | TextSDF = 4 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /source/Serializers/NumericsArrayConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Numerics; 3 | 4 | namespace NanoUI.Serializers 5 | { 6 | /// 7 | /// NumericsArrayConverter is used when serializing/deserializing to/from JSON. 8 | /// 9 | public static class NumericsArrayConverter 10 | { 11 | // Vector2 12 | public static Vector2 ToVector2(this float[] arr) 13 | { 14 | if (arr == null || arr.Length != 2) 15 | throw new ArgumentException("Array length must be 2"); 16 | 17 | return new Vector2(arr[0], arr[1]); 18 | } 19 | 20 | public static float[] ToArray(this Vector2 vec2) 21 | { 22 | return [ vec2.X, vec2.Y ]; 23 | } 24 | 25 | // Vector3 26 | public static float[] ToArray(this Vector3 vec3) 27 | { 28 | return [ vec3.X, vec3.Y, vec3.Z ]; 29 | } 30 | public static Vector3 ToVector3(this float[] arr) 31 | { 32 | if (arr == null || arr.Length != 3) 33 | throw new ArgumentException("Array length must be 3"); 34 | 35 | return new Vector3(arr[0], arr[1], arr[2]); 36 | } 37 | 38 | // Vector4 39 | public static float[] ToArray(this Vector4 vec4) 40 | { 41 | return [ vec4.X, vec4.Y, vec4.Z, vec4.W ]; 42 | } 43 | 44 | public static Vector4 ToVector4(this float[] arr) 45 | { 46 | if (arr == null || arr.Length != 4) 47 | throw new ArgumentException("Array length must be 4"); 48 | 49 | return new Vector4(arr[0], arr[1], arr[2], arr[3]); 50 | } 51 | 52 | // Quaternion 53 | public static float[] ToArray(this Quaternion quaternion) 54 | { 55 | return [ quaternion.X, quaternion.Y, quaternion.Z, quaternion.W ]; 56 | } 57 | 58 | public static Quaternion ToQuaternion(this float[] arr) 59 | { 60 | if (arr == null || arr.Length != 4) 61 | throw new ArgumentException("Array length must be 4"); 62 | 63 | return new Quaternion(arr[0], arr[1], arr[2], arr[3]); 64 | } 65 | 66 | // Matrix3x2 67 | public static float[] ToArray(this Matrix3x2 mat) 68 | { 69 | return 70 | [ 71 | mat.M11, mat.M12, 72 | mat.M21, mat.M22, 73 | mat.M31, mat.M32 74 | ]; 75 | } 76 | 77 | // Matrix4x4 78 | public static float[] ToArray(this Matrix4x4 mat) 79 | { 80 | return 81 | [ 82 | mat.M11, mat.M12, mat.M13, mat.M14, 83 | mat.M21, mat.M22, mat.M23, mat.M24, 84 | mat.M31, mat.M32, mat.M33, mat.M34, 85 | mat.M41, mat.M42, mat.M43, mat.M44, 86 | ]; 87 | } 88 | 89 | // Plane 90 | public static float[] ToArray(this Plane value) 91 | { 92 | return [ value.Normal.X, value.Normal.Y, value.Normal.Z, value.D ]; 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /source/Styles/BorderStyle.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Common; 2 | namespace NanoUI.Styles 3 | { 4 | /// 5 | /// Global border style. 6 | /// 7 | public struct BorderStyle 8 | { 9 | /// 10 | /// Dark 11 | /// 12 | public Color Dark { get; set; } 13 | 14 | /// 15 | /// Light 16 | /// 17 | public Color Light { get; set; } 18 | 19 | /// 20 | /// Medium 21 | /// 22 | public Color Medium { get; set; } 23 | 24 | /// 25 | /// Pointer focus color. 26 | /// 27 | public Color PointerFocus { get; set; } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /source/Styles/CommonStyle.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Common; 2 | 3 | namespace NanoUI.Styles 4 | { 5 | /// 6 | /// Global common style is used to set some widgets common properties 7 | /// in order to achieve some visual consistency. 8 | /// 9 | public struct CommonStyle 10 | { 11 | /// 12 | /// Accent color can be used to highlight some area. 13 | /// 14 | /// Not all widgets use this. 15 | public Color AccentColor { get; set; } 16 | 17 | /// 18 | /// Sunken background brush can be used to have some consistent, "sunken" look in widgets. 19 | /// 20 | public BrushBase? BackgroundSunken { get; set; } 21 | 22 | /// 23 | /// Disabled background brush. 24 | /// 25 | public BrushBase? BackgroundDisabled { get; set; } 26 | 27 | /// 28 | /// Invalid background brush is used to indicate invalid status 29 | /// (like invalid entry in UITextField). 30 | /// 31 | public BrushBase? BackgroundInvalid { get; set; } 32 | 33 | /// 34 | /// Background hover tint color. 35 | /// 36 | public Color? BackgroundHoverTint { get; set; } 37 | 38 | /// 39 | /// Frontend disabled color is used, 40 | /// when widget must draw widgets frontend (like text) disabled. 41 | /// 42 | public Color FrontendDisabledColor { get; set; } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /source/Styles/DockingStyle.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Common; 2 | using System.Numerics; 3 | 4 | namespace NanoUI.Styles 5 | { 6 | /// 7 | /// Global docking style is only used in docking. 8 | /// 9 | public struct DockingStyle 10 | { 11 | /// 12 | /// Hit area corner radius. 13 | /// 14 | public float HitAreaCornerRadius { get; set; } 15 | 16 | /// 17 | /// Hit area background color. 18 | /// 19 | public Color HitAreaBackgroundColor { get; set; } 20 | 21 | /// 22 | /// Hit area fill brush is for hit area visualization (left, top, right, bottom, center). 23 | /// 24 | public BrushBase? HitAreaFillBrush { get; set; } 25 | 26 | /// 27 | /// Docking overlay area color to show, where docking is going to be performed. 28 | /// 29 | public Color OverlayColor { get; set; } 30 | 31 | /// 32 | /// Title background focused brush. 33 | /// 34 | public BrushBase? TitleBackgroundFocused { get; set; } 35 | 36 | /// 37 | /// Title background unfocused brush. 38 | /// 39 | public BrushBase? TitleBackgroundUnfocused { get; set; } 40 | 41 | /// 42 | /// Title font type. 43 | /// 44 | public string? TitleFontType { get; set; } 45 | 46 | /// 47 | /// Title font size. 48 | /// 49 | public float TitleFontSize { get; set; } 50 | 51 | /// 52 | /// Title button size. 53 | /// 54 | public Vector2 TitleButtonSize { get; set; } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /source/Styles/FilesStyle.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Common; 2 | using System; 3 | 4 | namespace NanoUI.Styles 5 | { 6 | /// 7 | /// Global file style. 8 | /// 9 | public struct FilesStyle 10 | { 11 | /// 12 | /// Hard drive color. 13 | /// 14 | public Color HardDriveColor { get; set; } 15 | 16 | /// 17 | /// Folder color 18 | /// 19 | public Color FolderColor { get; set; } 20 | 21 | /// 22 | /// File color. 23 | /// 24 | public Color FileColor { get; set; } 25 | 26 | /// 27 | /// Column definitions for details (UIFileFolderDetails). 28 | /// 29 | public ColumnDefinition[] DetailsColumns { get; set; } = Array.Empty(); 30 | 31 | /// 32 | /// Column definitions for dropdown (UIFileFolderDropdown). 33 | /// 34 | public ColumnDefinition[] DropdownColumns { get; set; } = Array.Empty(); 35 | 36 | /// 37 | /// Column definitions for list (UIFileFolderList). 38 | /// 39 | public ColumnDefinition[] ListColumns { get; set; } = Array.Empty(); 40 | 41 | /// 42 | /// Column definitions for tree (UIFileFolderTree). 43 | /// 44 | public ColumnDefinition[] TreeColumns { get; set; } = Array.Empty(); 45 | 46 | public FilesStyle() { } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /source/Styles/PointerStyle.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Common; 2 | using NanoUI.Nvg; 3 | using System.Numerics; 4 | 5 | namespace NanoUI.Styles 6 | { 7 | /// 8 | /// Global pointer style 9 | /// 10 | public struct PointerStyle 11 | { 12 | /// 13 | /// Default pointer type when no other specified or ResetPointerType called. 14 | /// 15 | public int PointerType { get; set; } 16 | 17 | /// 18 | /// Pointer marker color can be used to "mark" position in the display. 19 | /// 20 | /// Can be used in post draw phase. 21 | public Color MarkerColor { get; set; } 22 | 23 | /// 24 | /// Radius for pointer marker circle. Default: 14. 25 | /// 26 | public float MarkerRadius { get; set; } = 14f; 27 | 28 | /// 29 | /// Constructor. 30 | /// 31 | public PointerStyle() { } 32 | 33 | /// 34 | /// Draws pointer marker circle with color & radius to given position. 35 | /// 36 | /// NvgContext 37 | /// Circle center 38 | public void DrawMarker(NvgContext ctx, Vector2 position) 39 | { 40 | ctx.BeginPath(); 41 | ctx.Circle(position, MarkerRadius); 42 | ctx.FillColor(MarkerColor); 43 | ctx.Fill(); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /source/Styles/ScrollbarStyle.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Common; 2 | 3 | namespace NanoUI.Styles 4 | { 5 | /// 6 | /// Global scrollbar style. 7 | /// Note: UIScrollbar is not themable widget, so we must have global style. 8 | /// 9 | public struct ScrollbarStyle 10 | { 11 | /// 12 | /// Scrollbar dimension. 13 | /// 14 | public uint ScrollbarDimension { get; set; } 15 | 16 | /// 17 | /// Background enabled brush. 18 | /// 19 | public BrushBase? BackgroundEnabled { get; set; } 20 | 21 | /// 22 | /// Slider brush. 23 | /// 24 | public BrushBase? SliderBrush { get; set; } 25 | 26 | /// 27 | /// Slider hover tint color. 28 | /// 29 | public Color SliderHoverTint { get; set; } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /source/Styles/WindowStyle.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Common; 2 | 3 | namespace NanoUI.Styles 4 | { 5 | /// 6 | /// Global window style has properties, that are same to all window instances. 7 | /// 8 | public struct WindowStyle 9 | { 10 | /// 11 | /// Border focused color. 12 | /// 13 | public Color BorderFocusedColor { get; set; } 14 | 15 | /// 16 | /// Border unfocused color. 17 | /// 18 | public Color BorderUnfocusedColor { get; set; } 19 | 20 | /// 21 | /// Border resizing color. 22 | /// 23 | public Color BorderResizingColor { get; set; } 24 | 25 | /// 26 | /// DragAreaWidth determines the area (border) width where we track if drag is happening. 27 | /// Default: 10, 28 | /// 29 | public uint DragAreaWidth { get; set; } = 10; 30 | 31 | /// 32 | /// Resizing triangle size in the UIWindows' bottom-right corner. Default: 25, 33 | /// 34 | public float ResizingTriangleSize { get; set; } = 25f; 35 | 36 | /// 37 | /// Constructor. 38 | /// 39 | public WindowStyle() { } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /source/Svg/Data/SvgGradientStop.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Common; 2 | 3 | namespace NanoUI.Svg.Data 4 | { 5 | internal struct SvgGradientStop 6 | { 7 | // defines where the gradient stop is placed along the gradient vector. 8 | public float Offset; 9 | // defines the color of the gradient stop. 10 | public Color StopColor; 11 | // defines the opacity of the gradient stop. 12 | public float StopOpacity; 13 | 14 | public bool IsPercent; 15 | } 16 | } -------------------------------------------------------------------------------- /source/Svg/Data/SvgLinearGradient.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Common; 2 | using NanoUI.Nvg.Data; 3 | using System; 4 | using System.Numerics; 5 | 6 | namespace NanoUI.Svg.Data 7 | { 8 | // note: we treat all values as percentages 9 | internal struct SvgLinearGradient 10 | { 11 | // Defines a unique id for the element 12 | public string id; 13 | // The x position of the starting point of the vector gradient. Default is 0% 14 | public float x1; 15 | // The x position of the ending point of the vector gradient. Default is 100% 16 | public float x2; 17 | // The y position of the starting point of the vector gradient. Default is 0% 18 | public float y1; 19 | // y2 The y position of the ending point of the vector gradient. Default is 0% 20 | public float y2; 21 | 22 | // collect stops here 23 | public SvgGradientStop[] Stops; 24 | 25 | public bool TryGetPaint(NvgBounds bounds, out Paint paint) 26 | { 27 | // not valid values 28 | if (Stops == null || Stops.Length < 2 && string.IsNullOrEmpty(id)) 29 | { 30 | paint = default; 31 | return false; 32 | } 33 | 34 | // convert to proportional values (range 0 .. 1) 35 | float sx = Math.Clamp(x1 / 100, 0, 1); 36 | float sy = Math.Clamp(y1 / 100, 0, 1); 37 | float ex = Math.Clamp(x2 / 100, 0, 1); 38 | float ey = Math.Clamp(y2 / 100, 0, 1); 39 | 40 | // convert to world space 41 | Vector2 start = bounds.Min + new Vector2(sx, sy) * (bounds.Max - bounds.Min); 42 | Vector2 end = bounds.Min + new Vector2(ex, ey) * (bounds.Max - bounds.Min); 43 | 44 | // get colors 45 | // start 46 | Color startColor = new Color(Stops[0].StopColor, Math.Clamp(Stops[0].StopOpacity, 0, 1)); 47 | // we support only 2 stops - take last 48 | Color endColor = new Color( 49 | Stops[Stops.Length - 1].StopColor, 50 | Math.Clamp(Stops[Stops.Length - 1].StopOpacity, 0, 1)); 51 | 52 | // create paint 53 | paint = Paint.LinearGradient(start, end, startColor, endColor); 54 | 55 | return true; 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /source/Svg/Data/SvgPath.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Nvg.Data; 2 | 3 | namespace NanoUI.Svg.Data 4 | { 5 | // this is the result - it is drawn in own BeginPath() function 6 | internal struct SvgPath 7 | { 8 | // collected commands 9 | public NvgPathCommand[] Commands; 10 | 11 | // style info (fill, stroke etc) 12 | public SvgStyle Style; 13 | 14 | public NvgBounds Bounds; 15 | 16 | // transform already applied when commands created 17 | } 18 | } -------------------------------------------------------------------------------- /source/Svg/Data/SvgRadialGradient.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Common; 2 | using NanoUI.Nvg.Data; 3 | using System; 4 | using System.Numerics; 5 | 6 | namespace NanoUI.Svg.Data 7 | { 8 | // note: we treat all values as percentages 9 | 10 | // todo: this is not entirely correct! 11 | internal struct SvgRadialGradient 12 | { 13 | // Defines a unique id for the element 14 | public string id; 15 | // The x position of the end circle of the radial gradient. Default is 50% 16 | public float cx; 17 | // The y position of the end circle of the radial gradient. Default is 50% 18 | public float cy; 19 | // The radius of the start circle of the radial gradient. Default is 0% 20 | //public float fr; 21 | // The x position of the start circle of the radial gradient. Default is 50% 22 | public float fx; 23 | // The y position of the start circle of the radial gradient. Default is 50% 24 | public float fy; 25 | // The radius of the end circle of the radial gradient. Default is 50% 26 | public float r; 27 | 28 | // collect stops here 29 | public SvgGradientStop[] Stops; 30 | 31 | public bool TryGetPaint(NvgBounds bounds, out Paint paint) 32 | { 33 | // not valid values 34 | if (Stops == null || Stops.Length < 2 && string.IsNullOrEmpty(id)) 35 | { 36 | paint = default; 37 | return false; 38 | } 39 | 40 | // convert to proportional values 41 | float _cx = Math.Clamp(cx / 100, 0, 1); 42 | float _cy = Math.Clamp(cy / 100, 0, 1); 43 | //float _r1 = Math.Clamp(fx / 100, 0, 1); 44 | //float _r2 = Math.Clamp(fy / 100, 0, 1); 45 | float _r = Math.Clamp(r / 100, 0, 1); 46 | 47 | // support misplaced center 48 | Vector2 center = bounds.Min + (new Vector2(_cx, _cy) * (bounds.Max - bounds.Min)); 49 | 50 | Vector2 rad = bounds.Min + new Vector2(_r) * (bounds.Max - bounds.Min); 51 | rad = new Vector2(_r) * (bounds.Max - bounds.Min); 52 | 53 | float inr = 0; 54 | float outr = rad.Y < rad.X? rad.Y : rad.X; 55 | 56 | Color innerColor = new Color(Stops[0].StopColor, Math.Clamp(Stops[0].StopOpacity, 0, 1)); 57 | Color outerColor = new Color( 58 | Stops[Stops.Length - 1].StopColor, 59 | Math.Clamp(Stops[Stops.Length - 1].StopOpacity, 0, 1)); 60 | 61 | // create paint 62 | paint = Paint.RadialGradient(center, inr, outr, innerColor, outerColor); 63 | 64 | return true; 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /source/Svg/Data/SvgShape.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Numerics; 3 | 4 | namespace NanoUI.Svg.Data 5 | { 6 | // collect here all params from & elements 7 | internal struct SvgShape 8 | { 9 | // this comes from svg xml file 10 | public Vector2 Size; 11 | 12 | public SvgPath[] Paths; 13 | 14 | // these are paths bounds 15 | public Vector2 PathsMin; 16 | public Vector2 PathsMax; 17 | 18 | public void Reset() 19 | { 20 | Size = Vector2.Zero; 21 | 22 | Paths = Array.Empty(); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /source/Svg/Data/SvgXmlAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace NanoUI.Svg.Data 4 | { 5 | /// 6 | /// Svg xml attribute 7 | /// 8 | public struct SvgXmlAttribute 9 | { 10 | /// 11 | /// Name 12 | /// 13 | public string Name; 14 | 15 | /// 16 | /// Value 17 | /// 18 | public object Value; 19 | 20 | /// 21 | /// AttributeType tells how we cast value. 22 | /// 23 | public Type AttributeType; 24 | 25 | /// 26 | /// Is percent? 27 | /// 28 | public bool IsPercent; 29 | 30 | /// 31 | /// Returns number value and flag if value was percent. 32 | /// 33 | /// Is percent? 34 | /// number value 35 | public float GetNumber(out bool isPercent) 36 | { 37 | isPercent = IsPercent; 38 | 39 | // check null & correct type 40 | if (Value != null && AttributeType == typeof(float)) 41 | { 42 | return (float)Value; 43 | } 44 | 45 | return default; 46 | } 47 | 48 | /// 49 | /// Returns value in type of T or default, if conversion can't be made. 50 | /// 51 | /// Type 52 | /// type of T or default, if conversion can't be made 53 | public T? GetValue() 54 | { 55 | // check null & correct type 56 | if(Value != null && AttributeType == typeof(T)) 57 | { 58 | return (T)Value; 59 | } 60 | 61 | return default; 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /source/Svg/Data/SvgXmlElement.cs: -------------------------------------------------------------------------------- 1 | using System.Numerics; 2 | 3 | namespace NanoUI.Svg.Data 4 | { 5 | // this is used to store "parent" element properties that affect all children element 6 | // it is stored in "stack" 7 | internal struct SvgXmlElement 8 | { 9 | public string? Id; 10 | public SvgStyle? Style; 11 | // transform property 12 | public Matrix3x2? Transform; 13 | 14 | //public SvgLinearGradient[]? LinearGradients; 15 | //public SvgRadialGradient[]? RadialGradients; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /source/Svg/Data/SvgXmlPath.cs: -------------------------------------------------------------------------------- 1 | namespace NanoUI.Svg.Data 2 | { 3 | /// 4 | /// Consists array of parsed commands from the xml. 5 | /// 6 | public struct SvgXmlPath 7 | { 8 | public SvgXmlPathCommand[] Commands; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /source/Svg/Data/SvgXmlPathCommand.cs: -------------------------------------------------------------------------------- 1 | namespace NanoUI.Svg.Data 2 | { 3 | /// 4 | /// Svg xml path command. 5 | /// 6 | public struct SvgXmlPathCommand 7 | { 8 | /// 9 | /// SvgXmlPathCommandType 10 | /// 11 | public SvgXmlPathCommandType CommandType; 12 | 13 | /// 14 | /// All values 15 | /// 16 | public float[] Values; 17 | 18 | /// 19 | /// Are values absolute or relative. 20 | /// 21 | public bool Absolute; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /source/Svg/Data/SvgXmlPathCommandType.cs: -------------------------------------------------------------------------------- 1 | namespace NanoUI.Svg.Data 2 | { 3 | /// 4 | /// SvgXmlPathCommandType 5 | /// 6 | public enum SvgXmlPathCommandType 7 | { 8 | /// 9 | /// M = moveto (move from one point to another point). 10 | /// 11 | MoveTo, 12 | 13 | /// 14 | /// L = lineto (create a line). 15 | /// 16 | LineTo, 17 | 18 | /// 19 | /// H = horizontal lineto (create a horizontal line). 20 | /// 21 | HorizontalLineTo, 22 | 23 | /// 24 | /// V = vertical lineto (create a vertical line). 25 | /// 26 | VerticalLineTo, 27 | 28 | /// 29 | /// C = curveto (create a curve). 30 | /// 31 | CurveTo, 32 | 33 | /// 34 | /// S = smooth curveto (create a smooth curve). 35 | /// 36 | SmoothCurveTo, 37 | 38 | /// 39 | /// Q = quadratic Bézier curve (create a quadratic Bézier curve). 40 | /// 41 | QuadraticBezier, 42 | 43 | /// 44 | /// T = smooth quadratic Bézier curveto (create a smooth quadratic Bézier curve). 45 | /// 46 | SmoothQuadraticBezier, 47 | 48 | /// 49 | /// A = elliptical Arc (create a elliptical arc). 50 | /// 51 | EllipticalArc, 52 | 53 | /// 54 | /// Z = closepath (close the path). 55 | /// 56 | ClosePath, 57 | 58 | /// 59 | /// None. 60 | /// 61 | NONE 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /source/Svg/Data/SvgXmlPoints.cs: -------------------------------------------------------------------------------- 1 | namespace NanoUI.Svg.Data 2 | { 3 | // todo: we could use Vector2 instead, since has always x & y 4 | 5 | /// 6 | /// Consists array of svg xml points 7 | /// 8 | public struct SvgXmlPoints 9 | { 10 | public float[] Points; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /source/Utils/ConvertUtils.cs: -------------------------------------------------------------------------------- 1 | using NanoUI.Common; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace NanoUI.Utils 6 | { 7 | /// 8 | /// ConvertUtils provides some common conversions. 9 | /// 10 | public static class ConvertUtils 11 | { 12 | static Dictionary _iconStrings = new(); 13 | 14 | /// 15 | /// Converts icon int to string and stores it. 16 | /// 17 | /// Prevents allocating new strings 18 | public static ReadOnlySpan GetIconString(int icon) 19 | { 20 | if (!_iconStrings.TryGetValue(icon, out var str)) 21 | { 22 | _iconStrings.Add(icon, char.ConvertFromUtf32(icon)); 23 | } 24 | 25 | return str; 26 | } 27 | 28 | /// 29 | /// Converts horizontal alignment to TextAlignment. 30 | /// 31 | /// TextHorizontalAlign 32 | /// TextAlignment 33 | public static TextAlignment ConvertHorizontalAlign(TextHorizontalAlign horizontalAlign) 34 | { 35 | switch (horizontalAlign) 36 | { 37 | case TextHorizontalAlign.Left: 38 | return TextAlignment.Left; 39 | case TextHorizontalAlign.Center: 40 | return TextAlignment.Center; 41 | case TextHorizontalAlign.Right: 42 | return TextAlignment.Right; 43 | } 44 | return TextAlignment.Left; 45 | } 46 | 47 | /// 48 | /// Converts vertical alignment to TextAlignment. 49 | /// 50 | /// 51 | /// 52 | public static TextAlignment ConvertVerticalAlign(TextVerticalAlign verticalAlign) 53 | { 54 | switch (verticalAlign) 55 | { 56 | case TextVerticalAlign.Top: 57 | return TextAlignment.Top; 58 | case TextVerticalAlign.Middle: 59 | return TextAlignment.Middle; 60 | case TextVerticalAlign.Bottom: 61 | return TextAlignment.Bottom; 62 | } 63 | return TextAlignment.Top; 64 | } 65 | 66 | /// 67 | /// Converts separate horizontalAlign & verticalAlign to 1 TextAlignment value. 68 | /// 69 | /// TextHorizontalAlign 70 | /// TextVerticalAlign 71 | /// TextAlignment 72 | public static TextAlignment ConvertTextAlign(TextHorizontalAlign horizontalAlign, TextVerticalAlign verticalAlign) 73 | { 74 | return ConvertHorizontalAlign(horizontalAlign) | ConvertVerticalAlign(verticalAlign); 75 | } 76 | } 77 | } 78 | --------------------------------------------------------------------------------