diff --git a/ImGui.Forms/Controls/ImageButton.cs b/ImGui.Forms/Controls/ImageButton.cs index a8a74af..c8c3b84 100644 --- a/ImGui.Forms/Controls/ImageButton.cs +++ b/ImGui.Forms/Controls/ImageButton.cs @@ -50,7 +50,7 @@ protected override void UpdateInternal(Rectangle contentRect) if ((IntPtr)Image != IntPtr.Zero) { - if ((ImGuiNET.ImGui.ImageButton((IntPtr)Image, GetImageSize()) || IsKeyDown(KeyAction)) && Enabled) + if ((ImGuiNET.ImGui.ImageButton($"##{Id}", (IntPtr)Image, GetImageSize()) || IsKeyDown(KeyAction)) && Enabled) OnClicked(); } else diff --git a/ImGui.Forms/Controls/Layouts/TableLayout.cs b/ImGui.Forms/Controls/Layouts/TableLayout.cs index b9272b9..ef02fb3 100644 --- a/ImGui.Forms/Controls/Layouts/TableLayout.cs +++ b/ImGui.Forms/Controls/Layouts/TableLayout.cs @@ -86,13 +86,13 @@ protected override void UpdateInternal(Rectangle contentRect) if (contentRect.Height < totalHeight) childFlags |= ImGuiWindowFlags.AlwaysVerticalScrollbar; - if (ImGuiNET.ImGui.BeginChild($"{Id}", contentRect.Size, false, childFlags)) + if (ImGuiNET.ImGui.BeginChild($"{Id}", contentRect.Size, ImGuiChildFlags.None, childFlags)) { var (x, y) = GetInitPoint(localWidths, localHeights, contentRect); ImGuiNET.ImGui.SetCursorPosX(x); ImGuiNET.ImGui.SetCursorPosY(y); - if (ImGuiNET.ImGui.BeginChild($"{Id}-in", new Vector2(totalWidth, totalHeight), false, ImGuiWindowFlags.NoScrollbar)) + if (ImGuiNET.ImGui.BeginChild($"{Id}-in", new Vector2(totalWidth, totalHeight), ImGuiChildFlags.None, ImGuiWindowFlags.NoScrollbar)) { (x, y) = (0, 0); var origX = x; @@ -155,13 +155,13 @@ protected override void UpdateInternal(Rectangle contentRect) ImGuiNET.ImGui.SetCursorPosX(x); ImGuiNET.ImGui.SetCursorPosY(y); - if (ImGuiNET.ImGui.BeginChild($"{Id}-{r}-{c}", new Vector2(cellWidth, cellHeight), false, ImGuiWindowFlags.NoScrollbar)) + if (ImGuiNET.ImGui.BeginChild($"{Id}-{r}-{c}", new Vector2(cellWidth, cellHeight), ImGuiChildFlags.None, ImGuiWindowFlags.NoScrollbar)) { // Draw cell content container ImGuiNET.ImGui.SetCursorPosX(xAdjust); ImGuiNET.ImGui.SetCursorPosY(yAdjust); - if (ImGuiNET.ImGui.BeginChild($"{Id}-{r}-{c}-content", new Vector2(cellInternalWidth, cellInternalHeight), false, ImGuiWindowFlags.NoScrollbar)) + if (ImGuiNET.ImGui.BeginChild($"{Id}-{r}-{c}-content", new Vector2(cellInternalWidth, cellInternalHeight), ImGuiChildFlags.None, ImGuiWindowFlags.NoScrollbar)) { // Draw component cell.Content?.Update(new Rectangle((int)(contentRect.X + x + xAdjust - sx), (int)(contentRect.Y + y + yAdjust - sy), cellInternalWidth, cellInternalHeight)); diff --git a/ImGui.Forms/Controls/Lists/BaseList.cs b/ImGui.Forms/Controls/Lists/BaseList.cs index 70984dd..fe8e20b 100644 --- a/ImGui.Forms/Controls/Lists/BaseList.cs +++ b/ImGui.Forms/Controls/Lists/BaseList.cs @@ -4,6 +4,7 @@ using System.Numerics; using ImGui.Forms.Controls.Base; using ImGui.Forms.Controls.Layouts; +using ImGuiNET; using Veldrid; using Size = ImGui.Forms.Models.Size; @@ -63,7 +64,7 @@ protected override void UpdateInternal(Rectangle contentRect) var listDimension = localItems.Sum(i => GetDimension(i, contentRect)) + Math.Max(0, localItems.Length - 1) * ItemSpacing + (int)(GetPadding() * 2); var scrollableDimension = GetScrollableDimension(contentRect); - if (ImGuiNET.ImGui.BeginChild($"{Id}", contentRect.Size, false)) + if (ImGuiNET.ImGui.BeginChild($"{Id}", contentRect.Size, ImGuiChildFlags.None)) { if (_scrollToLast) { @@ -136,7 +137,7 @@ private void SetScroll(float scroll) { scroll = Math.Max(0, scroll); - if(Alignment == Alignment.Vertical) + if (Alignment == Alignment.Vertical) ImGuiNET.ImGui.SetScrollY(scroll); else ImGuiNET.ImGui.SetScrollX(scroll); diff --git a/ImGui.Forms/Controls/Lists/ImageList.cs b/ImGui.Forms/Controls/Lists/ImageList.cs index 31343f0..99a155d 100644 --- a/ImGui.Forms/Controls/Lists/ImageList.cs +++ b/ImGui.Forms/Controls/Lists/ImageList.cs @@ -55,7 +55,7 @@ protected override void UpdateInternal(Rectangle contentRect) { ImageListItem selectedItem = null; - if (ImGuiNET.ImGui.BeginChild($"##{Id}_out", new Vector2(contentRect.Width, contentRect.Height), false)) + if (ImGuiNET.ImGui.BeginChild($"##{Id}_out", new Vector2(contentRect.Width, contentRect.Height), ImGuiChildFlags.None)) { var textHeight = FontResource.GetCurrentLineHeight(Font); @@ -66,7 +66,7 @@ protected override void UpdateInternal(Rectangle contentRect) var localItems = Items.ToArray(); - if (ImGuiNET.ImGui.BeginChild($"##{Id}_in", new Vector2(contentRect.Width, Padding.Y * 2 + localItems.Length * (itemHeight + ItemPadding)), false, ImGuiWindowFlags.NoScrollbar)) + if (ImGuiNET.ImGui.BeginChild($"##{Id}_in", new Vector2(contentRect.Width, Padding.Y * 2 + localItems.Length * (itemHeight + ItemPadding)), ImGuiChildFlags.None, ImGuiWindowFlags.NoScrollbar)) { for (var i = 0; i < localItems.Length; i++) { diff --git a/ImGui.Forms/Controls/MultiLineTextBox.cs b/ImGui.Forms/Controls/MultiLineTextBox.cs index a92da2d..1a60c60 100644 --- a/ImGui.Forms/Controls/MultiLineTextBox.cs +++ b/ImGui.Forms/Controls/MultiLineTextBox.cs @@ -1,6 +1,7 @@ using System; using ImGui.Forms.Controls.Base; using ImGui.Forms.Models; +using ImGuiNET; using Veldrid; namespace ImGui.Forms.Controls @@ -24,6 +25,11 @@ public string Text } } + /// + /// Marks the input as read-only. + /// + public bool IsReadOnly { get; set; } + /// /// Get or set the max count of characters allowed in the text box. /// @@ -41,8 +47,24 @@ public string Text protected override void UpdateInternal(Rectangle contentRect) { - if (ImGuiNET.ImGui.InputTextMultiline($"##{Id}", ref _text, MaxCharacters, contentRect.Size)) + var enabled = Enabled; + var isReadonly = IsReadOnly; + + var flags = ImGuiInputTextFlags.None; + if (isReadonly || !enabled) flags |= ImGuiInputTextFlags.ReadOnly; + + if (isReadonly || !enabled) + { + ImGuiNET.ImGui.PushStyleColor(ImGuiCol.FrameBg, 0xFF666666); + ImGuiNET.ImGui.PushStyleColor(ImGuiCol.FrameBgActive, 0xFF666666); + ImGuiNET.ImGui.PushStyleColor(ImGuiCol.FrameBgHovered, 0xFF666666); + } + + if (ImGuiNET.ImGui.InputTextMultiline($"##{Id}", ref _text, MaxCharacters, contentRect.Size, flags)) OnTextChanged(); + + if (isReadonly || !enabled) + ImGuiNET.ImGui.PopStyleColor(3); } private void OnTextChanged() diff --git a/ImGui.Forms/Controls/Tree/TreeView.cs b/ImGui.Forms/Controls/Tree/TreeView.cs index 35cc0c1..ae390fa 100644 --- a/ImGui.Forms/Controls/Tree/TreeView.cs +++ b/ImGui.Forms/Controls/Tree/TreeView.cs @@ -59,7 +59,7 @@ protected override void UpdateInternal(Rectangle contentRect) return; var anyNodeHovered = false; - if (ImGuiNET.ImGui.BeginChild($"{Id}", new Vector2(contentRect.Width, contentRect.Height), false, ImGuiWindowFlags.HorizontalScrollbar)) + if (ImGuiNET.ImGui.BeginChild($"{Id}", new Vector2(contentRect.Width, contentRect.Height), ImGuiChildFlags.None, ImGuiWindowFlags.HorizontalScrollbar)) { UpdateNodes(Nodes, ref anyNodeHovered); } diff --git a/ImGui.Forms/Factories/FontFactory.cs b/ImGui.Forms/Factories/FontFactory.cs index 2829b36..be41016 100644 --- a/ImGui.Forms/Factories/FontFactory.cs +++ b/ImGui.Forms/Factories/FontFactory.cs @@ -1,7 +1,10 @@ using System; using System.Collections.Generic; +using System.Drawing; using System.IO; +using System.Linq; using System.Reflection; +using ImGui.Forms.Models; using ImGui.Forms.Resources; using ImGui.Forms.Support.Veldrid.ImGui; using ImGuiNET; @@ -26,17 +29,17 @@ internal FontFactory() #region Registration - public void RegisterFromFile(string ttfPath, int size) + public void RegisterFromFile(string ttfPath, int size, FontGlyphRange glyphRanges = FontGlyphRange.All) { if (IsInitialized) throw new InvalidOperationException("Can not register new fonts after application started."); // If font not tracked, add it if (!_discCache.ContainsKey((ttfPath, size))) - _discCache[(ttfPath, size)] = new FontResource(ttfPath, size); + _discCache[(ttfPath, size)] = new FontResource(ttfPath, size, glyphRanges); } - public void RegisterFromResource(Assembly assembly, string resourceName, int size) + public void RegisterFromResource(Assembly assembly, string resourceName, int size, FontGlyphRange glyphRanges = FontGlyphRange.All) { if (IsInitialized) throw new InvalidOperationException("Can not register new fonts after application started."); @@ -62,7 +65,7 @@ public void RegisterFromResource(Assembly assembly, string resourceName, int siz return; // Otherwise add it to cache - _discCache[(fontPath, size)] = new FontResource(fontPath, size, true); + _discCache[(fontPath, size)] = new FontResource(fontPath, size, glyphRanges, true); } #endregion @@ -93,18 +96,82 @@ public FontResource Get(Assembly assembly, string resourceName, int size) #region Initialization - internal void Initialize(ImGuiIOPtr io, ImGuiRenderer controller) + internal unsafe void Initialize(ImGuiIOPtr io, ImGuiRenderer controller) { _io = io; _controller = controller; + // Initialize default font + var defaultFont = InitializeDefaultFont(); + // Initialize fonts + var config = ImGuiNative.ImFontConfig_ImFontConfig(); + config->MergeMode = 1; + foreach (var discFont in _discCache) - discFont.Value.Initialize(_io.Fonts.AddFontFromFileTTF(discFont.Key.Item1, discFont.Key.Item2)); + { + if (discFont.Value == defaultFont) + continue; + + var ranges = GetGlyphRanges(discFont.Value.GlyphRanges); + + var loadedFont = _io.Fonts.AddFontFromFileTTF(discFont.Key.Item1, discFont.Key.Item2, config, ranges.Data); + discFont.Value.Initialize(loadedFont); + } _controller.RecreateFontDeviceTexture(); } + private unsafe FontResource InitializeDefaultFont() + { + _io.Fonts.Clear(); + + ImFontPtr defaultFontPtr; + + var defaultFont = _discCache.Values.FirstOrDefault(f => f.GlyphRanges.HasFlag(FontGlyphRange.Default)); + if (defaultFont == null) + { + defaultFontPtr = _io.Fonts.AddFontDefault().NativePtr; + } + else + { + defaultFontPtr = _io.Fonts.AddFontFromFileTTF(defaultFont.Path, defaultFont.Size, null, GetGlyphRanges(defaultFont.GlyphRanges).Data); + defaultFont.Initialize(defaultFontPtr); + } + + var config = ImGuiNative.ImFontConfig_ImFontConfig(); + config->DstFont = defaultFontPtr; + + return defaultFont; + } + + private unsafe ImVector GetGlyphRanges(FontGlyphRange rangeFlags) + { + var builder = new ImFontGlyphRangesBuilderPtr(ImGuiNative.ImFontGlyphRangesBuilder_ImFontGlyphRangesBuilder()); + builder.Clear(); + + if (rangeFlags.HasFlag(FontGlyphRange.Default)) + builder.AddRanges(_io.Fonts.GetGlyphRangesDefault()); + if (rangeFlags.HasFlag(FontGlyphRange.Cyrillic)) + builder.AddRanges(_io.Fonts.GetGlyphRangesCyrillic()); + if (rangeFlags.HasFlag(FontGlyphRange.Chinese)) + builder.AddRanges(_io.Fonts.GetGlyphRangesChineseFull()); + if (rangeFlags.HasFlag(FontGlyphRange.Japanese)) + builder.AddRanges(_io.Fonts.GetGlyphRangesJapanese()); + if (rangeFlags.HasFlag(FontGlyphRange.Greek)) + builder.AddRanges(_io.Fonts.GetGlyphRangesGreek()); + if (rangeFlags.HasFlag(FontGlyphRange.Korean)) + builder.AddRanges(_io.Fonts.GetGlyphRangesKorean()); + if (rangeFlags.HasFlag(FontGlyphRange.Thai)) + builder.AddRanges(_io.Fonts.GetGlyphRangesThai()); + if (rangeFlags.HasFlag(FontGlyphRange.Vietnamese)) + builder.AddRanges(_io.Fonts.GetGlyphRangesVietnamese()); + + builder.BuildRanges(out var ranges); + + return ranges; + } + #endregion public void Dispose() diff --git a/ImGui.Forms/ImGui.Forms.csproj b/ImGui.Forms/ImGui.Forms.csproj index f058ede..61d41f5 100644 --- a/ImGui.Forms/ImGui.Forms.csproj +++ b/ImGui.Forms/ImGui.Forms.csproj @@ -18,18 +18,10 @@ - - - - arial.ttf - - - roboto.ttf - error.png @@ -78,7 +70,7 @@ - + diff --git a/ImGui.Forms/ImGui.Forms.nuspec b/ImGui.Forms/ImGui.Forms.nuspec index 16f4a86..dfc46cc 100644 --- a/ImGui.Forms/ImGui.Forms.nuspec +++ b/ImGui.Forms/ImGui.Forms.nuspec @@ -2,7 +2,7 @@ Imgui.Forms - 1.0.45 + 1.0.46 A WinForms-inspired object-oriented framework around Dear ImGui (https://github.com/ocornut/imgui) onepiecefreak @@ -19,7 +19,7 @@ - + diff --git a/ImGui.Forms/Models/FontGlyphRange.cs b/ImGui.Forms/Models/FontGlyphRange.cs new file mode 100644 index 0000000..c0fd800 --- /dev/null +++ b/ImGui.Forms/Models/FontGlyphRange.cs @@ -0,0 +1,19 @@ +using System; + +namespace ImGui.Forms.Models +{ + [Flags] + public enum FontGlyphRange + { + Default = 1 << 0, + Cyrillic = 1 << 1, + Chinese = 1 << 2, + Japanese = 1 << 3, + Greek = 1 << 4, + Korean = 1 << 5, + Thai = 1 << 6, + Vietnamese = 1 << 7, + + All = 255, + } +} diff --git a/ImGui.Forms/Resources/FontResource.cs b/ImGui.Forms/Resources/FontResource.cs index b49204e..4021d1d 100644 --- a/ImGui.Forms/Resources/FontResource.cs +++ b/ImGui.Forms/Resources/FontResource.cs @@ -1,6 +1,7 @@ using System; using System.IO; using System.Numerics; +using ImGui.Forms.Models; using ImGuiNET; namespace ImGui.Forms.Resources @@ -12,20 +13,29 @@ namespace ImGui.Forms.Resources public class FontResource : IDisposable { private ImFontPtr _ptr; - - private readonly string _path; + private readonly bool _temporary; + /// + /// Supported glyphs. + /// + public FontGlyphRange GlyphRanges { get; } + + /// + /// The physical path to the font. + /// + public string Path { get; } /// /// The size of the font. /// public int Size { get; } - internal FontResource(string path, int size, bool temporary = false) + internal FontResource(string path, int size, FontGlyphRange glyphRanges, bool temporary = false) { - _path = path; + Path = path; Size = size; _temporary = temporary; + GlyphRanges = glyphRanges; } internal void Initialize(ImFontPtr ptr) @@ -42,8 +52,8 @@ protected virtual void Dispose(bool disposing) if (!_temporary) return; - if (File.Exists(_path)) - File.Delete(_path); + if (File.Exists(Path)) + File.Delete(Path); } /// diff --git a/ImGui.Forms/Resources/FontResources.cs b/ImGui.Forms/Resources/FontResources.cs deleted file mode 100644 index 485567d..0000000 --- a/ImGui.Forms/Resources/FontResources.cs +++ /dev/null @@ -1,50 +0,0 @@ -namespace ImGui.Forms.Resources -{ - /// - /// Allows access to built-in fonts. - /// - /// To load and use your own fonts, see and . - public static class FontResources - { - private const string ArialPath_ = "arial.ttf"; - private const string RobotoPath_ = "roboto.ttf"; - - /// - /// Registers the font "Arial" with into the application. Has to be called before the application was started. - /// - /// The size to display the font in. - public static void RegisterArial(int size) - { - Application.FontFactory.RegisterFromResource(typeof(FontResources).Assembly, ArialPath_, size); - } - - /// - /// Registers the font "Roboto" with into the application. Has to be called before the application was started. - /// - /// The size to display the font in. - public static void RegisterRoboto(int size) - { - Application.FontFactory.RegisterFromResource(typeof(FontResources).Assembly, RobotoPath_, size); - } - - /// - /// Gets a representing "Arial" with . Has to be registered with before the application was started. - /// - /// The size to display the font in. - /// A representing "Arial" in the given . - public static FontResource Arial(int size) - { - return Application.FontFactory.Get(typeof(FontResources).Assembly, ArialPath_, size); - } - - /// - /// Gets a representing "Roboto" with . Has to be registered with before the application was started. - /// - /// - /// A representing "Roboto" in the given . - public static FontResource Roboto(int size) - { - return Application.FontFactory.Get(typeof(FontResources).Assembly, RobotoPath_, size); - } - } -} diff --git a/ImGui.Forms/Resources/Fonts/arial.ttf b/ImGui.Forms/Resources/Fonts/arial.ttf deleted file mode 100644 index ff0815c..0000000 Binary files a/ImGui.Forms/Resources/Fonts/arial.ttf and /dev/null differ diff --git a/ImGui.Forms/Resources/Fonts/roboto.ttf b/ImGui.Forms/Resources/Fonts/roboto.ttf deleted file mode 100644 index 39c63d7..0000000 Binary files a/ImGui.Forms/Resources/Fonts/roboto.ttf and /dev/null differ diff --git a/ImGui.Forms/Support/Veldrid.ImGui/ImGuiRenderer.cs b/ImGui.Forms/Support/Veldrid.ImGui/ImGuiRenderer.cs index fa91e49..98b2892 100644 --- a/ImGui.Forms/Support/Veldrid.ImGui/ImGuiRenderer.cs +++ b/ImGui.Forms/Support/Veldrid.ImGui/ImGuiRenderer.cs @@ -32,9 +32,6 @@ public class ImGuiRenderer : IDisposable private ResourceSet _mainResourceSet; private ResourceSet _fontTextureResourceSet; private IntPtr _fontAtlasID = (IntPtr)1; - private bool _controlDown; - private bool _shiftDown; - private bool _altDown; private int _windowWidth; private int _windowHeight; @@ -43,8 +40,10 @@ public class ImGuiRenderer : IDisposable // Image trackers private readonly Dictionary _setsByView = new Dictionary(); + private readonly Dictionary _autoViewsByTexture = new Dictionary(); + private readonly Dictionary _viewsById = new Dictionary(); private readonly List _ownedResources = new List(); private int _lastAssignedID = 100; @@ -58,7 +57,9 @@ private readonly Dictionary _autoViewsByTexture /// The initial width of the rendering target. Can be resized. /// The initial height of the rendering target. Can be resized. public ImGuiRenderer(GraphicsDevice gd, OutputDescription outputDescription, int width, int height) - : this(gd, outputDescription, width, height, ColorSpaceHandling.Legacy) { } + : this(gd, outputDescription, width, height, ColorSpaceHandling.Legacy) + { + } /// /// Constructs a new ImGuiRenderer. @@ -68,7 +69,8 @@ public ImGuiRenderer(GraphicsDevice gd, OutputDescription outputDescription, int /// The initial width of the rendering target. Can be resized. /// The initial height of the rendering target. Can be resized. /// Identifies how the renderer should treat vertex colors. - public ImGuiRenderer(GraphicsDevice gd, OutputDescription outputDescription, int width, int height, ColorSpaceHandling colorSpaceHandling) + public ImGuiRenderer(GraphicsDevice gd, OutputDescription outputDescription, int width, int height, + ColorSpaceHandling colorSpaceHandling) { _gd = gd; _assembly = typeof(ImGuiRenderer).GetTypeInfo().Assembly; @@ -79,10 +81,10 @@ public ImGuiRenderer(GraphicsDevice gd, OutputDescription outputDescription, int IntPtr context = ImGuiNET.ImGui.CreateContext(); ImGuiNET.ImGui.SetCurrentContext(context); - ImGuiNET.ImGui.GetIO().Fonts.AddFontDefault(); + //ImGuiNET.ImGui.GetIO().Fonts.AddFontDefault(); + //ImGuiNET.ImGui.GetIO().Fonts.Flags |= ImFontAtlasFlags.NoBakedLines; CreateDeviceResources(gd, outputDescription); - SetOpenTKKeyMappings(); SetPerFrameImGuiData(1f / 60f); @@ -103,42 +105,61 @@ public void DestroyDeviceObjects() public void CreateDeviceResources(GraphicsDevice gd, OutputDescription outputDescription) => CreateDeviceResources(gd, outputDescription, _colorSpaceHandling); - public void CreateDeviceResources(GraphicsDevice gd, OutputDescription outputDescription, ColorSpaceHandling colorSpaceHandling) + + public void CreateDeviceResources(GraphicsDevice gd, OutputDescription outputDescription, + ColorSpaceHandling colorSpaceHandling) { _gd = gd; _colorSpaceHandling = colorSpaceHandling; ResourceFactory factory = gd.ResourceFactory; - _vertexBuffer = factory.CreateBuffer(new BufferDescription(10000, BufferUsage.VertexBuffer | BufferUsage.Dynamic)); + _vertexBuffer = + factory.CreateBuffer(new BufferDescription(10000, BufferUsage.VertexBuffer | BufferUsage.Dynamic)); _vertexBuffer.Name = "ImGui.NET Vertex Buffer"; - _indexBuffer = factory.CreateBuffer(new BufferDescription(2000, BufferUsage.IndexBuffer | BufferUsage.Dynamic)); + _indexBuffer = + factory.CreateBuffer(new BufferDescription(2000, BufferUsage.IndexBuffer | BufferUsage.Dynamic)); _indexBuffer.Name = "ImGui.NET Index Buffer"; - _projMatrixBuffer = factory.CreateBuffer(new BufferDescription(64, BufferUsage.UniformBuffer | BufferUsage.Dynamic)); + _projMatrixBuffer = + factory.CreateBuffer(new BufferDescription(64, BufferUsage.UniformBuffer | BufferUsage.Dynamic)); _projMatrixBuffer.Name = "ImGui.NET Projection Buffer"; - byte[] vertexShaderBytes = LoadEmbeddedShaderCode(gd.ResourceFactory, "imgui-vertex", ShaderStages.Vertex, _colorSpaceHandling); - byte[] fragmentShaderBytes = LoadEmbeddedShaderCode(gd.ResourceFactory, "imgui-frag", ShaderStages.Fragment, _colorSpaceHandling); - _vertexShader = factory.CreateShader(new ShaderDescription(ShaderStages.Vertex, vertexShaderBytes, _gd.BackendType == GraphicsBackend.Vulkan ? "main" : "VS")); - _fragmentShader = factory.CreateShader(new ShaderDescription(ShaderStages.Fragment, fragmentShaderBytes, _gd.BackendType == GraphicsBackend.Vulkan ? "main" : "FS")); + byte[] vertexShaderBytes = LoadEmbeddedShaderCode(gd.ResourceFactory, "imgui-vertex", ShaderStages.Vertex, + _colorSpaceHandling); + byte[] fragmentShaderBytes = LoadEmbeddedShaderCode(gd.ResourceFactory, "imgui-frag", ShaderStages.Fragment, + _colorSpaceHandling); + _vertexShader = factory.CreateShader(new ShaderDescription(ShaderStages.Vertex, vertexShaderBytes, + _gd.BackendType == GraphicsBackend.Vulkan ? "main" : "VS")); + _vertexShader.Name = "ImGui.NET Vertex Shader"; + _fragmentShader = factory.CreateShader(new ShaderDescription(ShaderStages.Fragment, fragmentShaderBytes, + _gd.BackendType == GraphicsBackend.Vulkan ? "main" : "FS")); + _fragmentShader.Name = "ImGui.NET Fragment Shader"; VertexLayoutDescription[] vertexLayouts = new VertexLayoutDescription[] { new VertexLayoutDescription( - new VertexElementDescription("in_position", VertexElementSemantic.Position, VertexElementFormat.Float2), - new VertexElementDescription("in_texCoord", VertexElementSemantic.TextureCoordinate, VertexElementFormat.Float2), - new VertexElementDescription("in_color", VertexElementSemantic.Color, VertexElementFormat.Byte4_Norm)) + new VertexElementDescription("in_position", VertexElementSemantic.Position, + VertexElementFormat.Float2), + new VertexElementDescription("in_texCoord", VertexElementSemantic.TextureCoordinate, + VertexElementFormat.Float2), + new VertexElementDescription("in_color", VertexElementSemantic.Color, + VertexElementFormat.Byte4_Norm)) }; _layout = factory.CreateResourceLayout(new ResourceLayoutDescription( - new ResourceLayoutElementDescription("ProjectionMatrixBuffer", ResourceKind.UniformBuffer, ShaderStages.Vertex), + new ResourceLayoutElementDescription("ProjectionMatrixBuffer", ResourceKind.UniformBuffer, + ShaderStages.Vertex), new ResourceLayoutElementDescription("MainSampler", ResourceKind.Sampler, ShaderStages.Fragment))); + _layout.Name = "ImGui.NET Resource Layout"; _textureLayout = factory.CreateResourceLayout(new ResourceLayoutDescription( - new ResourceLayoutElementDescription("MainTexture", ResourceKind.TextureReadOnly, ShaderStages.Fragment))); + new ResourceLayoutElementDescription("MainTexture", ResourceKind.TextureReadOnly, + ShaderStages.Fragment))); + _textureLayout.Name = "ImGui.NET Texture Layout"; GraphicsPipelineDescription pd = new GraphicsPipelineDescription( BlendStateDescription.SingleAlphaBlend, new DepthStencilStateDescription(false, false, ComparisonKind.Always), - new RasterizerStateDescription(FaceCullMode.None, PolygonFillMode.Solid, FrontFace.Clockwise, true, true), + new RasterizerStateDescription(FaceCullMode.None, PolygonFillMode.Solid, FrontFace.Clockwise, true, + true), PrimitiveTopology.TriangleList, new ShaderSetDescription( vertexLayouts, @@ -152,10 +173,12 @@ public void CreateDeviceResources(GraphicsDevice gd, OutputDescription outputDes outputDescription, ResourceBindingModel.Default); _pipeline = factory.CreateGraphicsPipeline(ref pd); + _pipeline.Name = "ImGui.NET Pipeline"; _mainResourceSet = factory.CreateResourceSet(new ResourceSetDescription(_layout, _projMatrixBuffer, gd.PointSampler)); + _mainResourceSet.Name = "ImGui.NET Main Resource Set"; RecreateFontDeviceTexture(gd); } @@ -168,7 +191,9 @@ public IntPtr GetOrCreateImGuiBinding(ResourceFactory factory, TextureView textu { if (!_setsByView.TryGetValue(textureView, out ResourceSetInfo rsi)) { - ResourceSet resourceSet = factory.CreateResourceSet(new ResourceSetDescription(_textureLayout, textureView)); + ResourceSet resourceSet = + factory.CreateResourceSet(new ResourceSetDescription(_textureLayout, textureView)); + resourceSet.Name = $"ImGui.NET {textureView.Name} Resource Set"; rsi = new ResourceSetInfo(GetNextImGuiBindingID(), resourceSet); _setsByView.Add(textureView, rsi); @@ -205,6 +230,7 @@ public IntPtr GetOrCreateImGuiBinding(ResourceFactory factory, Texture texture) if (!_autoViewsByTexture.TryGetValue(texture, out TextureView textureView)) { textureView = factory.CreateTextureView(texture); + textureView.Name = $"ImGui.NET {texture.Name} View"; _autoViewsByTexture.Add(texture, textureView); _ownedResources.Add(textureView); } @@ -259,33 +285,45 @@ private byte[] LoadEmbeddedShaderCode( switch (factory.BackendType) { case GraphicsBackend.Direct3D11: - { - if (stage == ShaderStages.Vertex && colorSpaceHandling == ColorSpaceHandling.Legacy) { name += "-legacy"; } - string resourceName = name + ".hlsl.bytes"; - return GetEmbeddedResourceBytes(resourceName); - } + { + if (stage == ShaderStages.Vertex && colorSpaceHandling == ColorSpaceHandling.Legacy) + { + name += "-legacy"; + } + + string resourceName = name + ".hlsl.bytes"; + return GetEmbeddedResourceBytes(resourceName); + } case GraphicsBackend.OpenGL: - { - if (stage == ShaderStages.Vertex && colorSpaceHandling == ColorSpaceHandling.Legacy) { name += "-legacy"; } - string resourceName = name + ".glsl"; - return GetEmbeddedResourceBytes(resourceName); - } + { + if (stage == ShaderStages.Vertex && colorSpaceHandling == ColorSpaceHandling.Legacy) + { + name += "-legacy"; + } + + string resourceName = name + ".glsl"; + return GetEmbeddedResourceBytes(resourceName); + } case GraphicsBackend.OpenGLES: - { - if (stage == ShaderStages.Vertex && colorSpaceHandling == ColorSpaceHandling.Legacy) { name += "-legacy"; } - string resourceName = name + ".glsles"; - return GetEmbeddedResourceBytes(resourceName); - } + { + if (stage == ShaderStages.Vertex && colorSpaceHandling == ColorSpaceHandling.Legacy) + { + name += "-legacy"; + } + + string resourceName = name + ".glsles"; + return GetEmbeddedResourceBytes(resourceName); + } case GraphicsBackend.Vulkan: - { - string resourceName = name + ".spv"; - return GetEmbeddedResourceBytes(resourceName); - } + { + string resourceName = name + ".spv"; + return GetEmbeddedResourceBytes(resourceName); + } case GraphicsBackend.Metal: - { - string resourceName = name + ".metallib"; - return GetEmbeddedResourceBytes(resourceName); - } + { + string resourceName = name + ".metallib"; + return GetEmbeddedResourceBytes(resourceName); + } default: throw new NotImplementedException(); } @@ -319,7 +357,7 @@ private byte[] GetEmbeddedResourceBytes(string resourceName) /// public unsafe void RecreateFontDeviceTexture(GraphicsDevice gd) { - ImGuiNET.ImGuiIOPtr io = ImGuiNET.ImGui.GetIO(); + ImGuiIOPtr io = ImGuiNET.ImGui.GetIO(); // Build io.Fonts.GetTexDataAsRGBA32(out byte* pixels, out int width, out int height, out int bytesPerPixel); @@ -349,7 +387,9 @@ public unsafe void RecreateFontDeviceTexture(GraphicsDevice gd) 0); _fontTextureResourceSet?.Dispose(); - _fontTextureResourceSet = gd.ResourceFactory.CreateResourceSet(new ResourceSetDescription(_textureLayout, _fontTexture)); + _fontTextureResourceSet = + gd.ResourceFactory.CreateResourceSet(new ResourceSetDescription(_textureLayout, _fontTexture)); + _fontTextureResourceSet.Name = "ImGui.NET Font Texture Resource Set"; io.Fonts.ClearTexData(); } @@ -415,94 +455,198 @@ private unsafe void SetPerFrameImGuiData(float deltaSeconds) io.DeltaTime = deltaSeconds; // DeltaTime is in seconds. } - private unsafe void UpdateImGuiInput(InputSnapshot snapshot) + private bool TryMapKey(Key key, out ImGuiKey result) { - ImGuiIOPtr io = ImGuiNET.ImGui.GetIO(); - - // Determine if any of the mouse buttons were pressed during this snapshot period, even if they are no longer held. - bool leftPressed = false; - bool middlePressed = false; - bool rightPressed = false; - for (int i = 0; i < snapshot.MouseEvents.Count; i++) + ImGuiKey keyToImGuiKeyShortcut(Key keyToConvert, Key startKey1, ImGuiKey startKey2) { - MouseEvent me = snapshot.MouseEvents[i]; - if (me.Down) - { - switch (me.MouseButton) - { - case MouseButton.Left: - leftPressed = true; - break; - case MouseButton.Middle: - middlePressed = true; - break; - case MouseButton.Right: - rightPressed = true; - break; - } - } + int changeFromStart1 = (int)keyToConvert - (int)startKey1; + return startKey2 + changeFromStart1; } - io.MouseDown[0] = leftPressed || snapshot.IsMouseDown(MouseButton.Left); - io.MouseDown[1] = rightPressed || snapshot.IsMouseDown(MouseButton.Right); - io.MouseDown[2] = middlePressed || snapshot.IsMouseDown(MouseButton.Middle); - io.MousePos = snapshot.MousePosition; - io.MouseWheel = snapshot.WheelDelta; - - IReadOnlyList keyCharPresses = snapshot.KeyCharPresses; - for (int i = 0; i < keyCharPresses.Count; i++) + if (key >= Key.F1 && key <= Key.F12) { - char c = keyCharPresses[i]; - ImGuiNET.ImGui.GetIO().AddInputCharacter(c); + result = keyToImGuiKeyShortcut(key, Key.F1, ImGuiKey.F1); + return true; } - - IReadOnlyList keyEvents = snapshot.KeyEvents; - for (int i = 0; i < keyEvents.Count; i++) + else if (key >= Key.Keypad0 && key <= Key.Keypad9) { - KeyEvent keyEvent = keyEvents[i]; - io.KeysDown[(int)keyEvent.Key] = keyEvent.Down; - if (keyEvent.Key == Key.ControlLeft) - { - _controlDown = keyEvent.Down; - } - if (keyEvent.Key == Key.ShiftLeft) - { - _shiftDown = keyEvent.Down; - } - if (keyEvent.Key == Key.AltLeft) - { - _altDown = keyEvent.Down; - } + result = keyToImGuiKeyShortcut(key, Key.Keypad0, ImGuiKey.Keypad0); + return true; + } + else if (key >= Key.A && key <= Key.Z) + { + result = keyToImGuiKeyShortcut(key, Key.A, ImGuiKey.A); + return true; + } + else if (key >= Key.Number0 && key <= Key.Number9) + { + result = keyToImGuiKeyShortcut(key, Key.Number0, ImGuiKey._0); + return true; } - io.KeyCtrl = _controlDown; - io.KeyAlt = _altDown; - io.KeyShift = _shiftDown; + switch (key) + { + case Key.ShiftLeft: + case Key.ShiftRight: + result = ImGuiKey.ModShift; + return true; + case Key.ControlLeft: + case Key.ControlRight: + result = ImGuiKey.ModCtrl; + return true; + case Key.AltLeft: + case Key.AltRight: + result = ImGuiKey.ModAlt; + return true; + case Key.WinLeft: + case Key.WinRight: + result = ImGuiKey.ModSuper; + return true; + case Key.Menu: + result = ImGuiKey.Menu; + return true; + case Key.Up: + result = ImGuiKey.UpArrow; + return true; + case Key.Down: + result = ImGuiKey.DownArrow; + return true; + case Key.Left: + result = ImGuiKey.LeftArrow; + return true; + case Key.Right: + result = ImGuiKey.RightArrow; + return true; + case Key.Enter: + result = ImGuiKey.Enter; + return true; + case Key.Escape: + result = ImGuiKey.Escape; + return true; + case Key.Space: + result = ImGuiKey.Space; + return true; + case Key.Tab: + result = ImGuiKey.Tab; + return true; + case Key.BackSpace: + result = ImGuiKey.Backspace; + return true; + case Key.Insert: + result = ImGuiKey.Insert; + return true; + case Key.Delete: + result = ImGuiKey.Delete; + return true; + case Key.PageUp: + result = ImGuiKey.PageUp; + return true; + case Key.PageDown: + result = ImGuiKey.PageDown; + return true; + case Key.Home: + result = ImGuiKey.Home; + return true; + case Key.End: + result = ImGuiKey.End; + return true; + case Key.CapsLock: + result = ImGuiKey.CapsLock; + return true; + case Key.ScrollLock: + result = ImGuiKey.ScrollLock; + return true; + case Key.PrintScreen: + result = ImGuiKey.PrintScreen; + return true; + case Key.Pause: + result = ImGuiKey.Pause; + return true; + case Key.NumLock: + result = ImGuiKey.NumLock; + return true; + case Key.KeypadDivide: + result = ImGuiKey.KeypadDivide; + return true; + case Key.KeypadMultiply: + result = ImGuiKey.KeypadMultiply; + return true; + case Key.KeypadSubtract: + result = ImGuiKey.KeypadSubtract; + return true; + case Key.KeypadAdd: + result = ImGuiKey.KeypadAdd; + return true; + case Key.KeypadDecimal: + result = ImGuiKey.KeypadDecimal; + return true; + case Key.KeypadEnter: + result = ImGuiKey.KeypadEnter; + return true; + case Key.Tilde: + result = ImGuiKey.GraveAccent; + return true; + case Key.Minus: + result = ImGuiKey.Minus; + return true; + case Key.Plus: + result = ImGuiKey.Equal; + return true; + case Key.BracketLeft: + result = ImGuiKey.LeftBracket; + return true; + case Key.BracketRight: + result = ImGuiKey.RightBracket; + return true; + case Key.Semicolon: + result = ImGuiKey.Semicolon; + return true; + case Key.Quote: + result = ImGuiKey.Apostrophe; + return true; + case Key.Comma: + result = ImGuiKey.Comma; + return true; + case Key.Period: + result = ImGuiKey.Period; + return true; + case Key.Slash: + result = ImGuiKey.Slash; + return true; + case Key.BackSlash: + case Key.NonUSBackSlash: + result = ImGuiKey.Backslash; + return true; + default: + result = ImGuiKey.GamepadBack; + return false; + } } - private static unsafe void SetOpenTKKeyMappings() + private unsafe void UpdateImGuiInput(InputSnapshot snapshot) { ImGuiIOPtr io = ImGuiNET.ImGui.GetIO(); - io.KeyMap[(int)ImGuiKey.Tab] = (int)Key.Tab; - io.KeyMap[(int)ImGuiKey.LeftArrow] = (int)Key.Left; - io.KeyMap[(int)ImGuiKey.RightArrow] = (int)Key.Right; - io.KeyMap[(int)ImGuiKey.UpArrow] = (int)Key.Up; - io.KeyMap[(int)ImGuiKey.DownArrow] = (int)Key.Down; - io.KeyMap[(int)ImGuiKey.PageUp] = (int)Key.PageUp; - io.KeyMap[(int)ImGuiKey.PageDown] = (int)Key.PageDown; - io.KeyMap[(int)ImGuiKey.Home] = (int)Key.Home; - io.KeyMap[(int)ImGuiKey.End] = (int)Key.End; - io.KeyMap[(int)ImGuiKey.Delete] = (int)Key.Delete; - io.KeyMap[(int)ImGuiKey.Backspace] = (int)Key.BackSpace; - io.KeyMap[(int)ImGuiKey.Enter] = (int)Key.Enter; - io.KeyMap[(int)ImGuiKey.Escape] = (int)Key.Escape; - io.KeyMap[(int)ImGuiKey.Space] = (int)Key.Space; - io.KeyMap[(int)ImGuiKey.A] = (int)Key.A; - io.KeyMap[(int)ImGuiKey.C] = (int)Key.C; - io.KeyMap[(int)ImGuiKey.V] = (int)Key.V; - io.KeyMap[(int)ImGuiKey.X] = (int)Key.X; - io.KeyMap[(int)ImGuiKey.Y] = (int)Key.Y; - io.KeyMap[(int)ImGuiKey.Z] = (int)Key.Z; + io.AddMousePosEvent(snapshot.MousePosition.X, snapshot.MousePosition.Y); + io.AddMouseButtonEvent(0, snapshot.IsMouseDown(MouseButton.Left)); + io.AddMouseButtonEvent(1, snapshot.IsMouseDown(MouseButton.Right)); + io.AddMouseButtonEvent(2, snapshot.IsMouseDown(MouseButton.Middle)); + io.AddMouseButtonEvent(3, snapshot.IsMouseDown(MouseButton.Button1)); + io.AddMouseButtonEvent(4, snapshot.IsMouseDown(MouseButton.Button2)); + io.AddMouseWheelEvent(0f, snapshot.WheelDelta); + + for (int i = 0; i < snapshot.KeyCharPresses.Count; i++) + { + io.AddInputCharacter(snapshot.KeyCharPresses[i]); + } + + for (int i = 0; i < snapshot.KeyEvents.Count; i++) + { + KeyEvent keyEvent = snapshot.KeyEvents[i]; + if (TryMapKey(keyEvent.Key, out ImGuiKey imguikey)) + { + io.AddKeyEvent(imguikey, keyEvent.Down); + } + } } private unsafe void RenderImDrawData(ImDrawDataPtr draw_data, GraphicsDevice gd, CommandList cl) @@ -519,19 +663,23 @@ private unsafe void RenderImDrawData(ImDrawDataPtr draw_data, GraphicsDevice gd, if (totalVBSize > _vertexBuffer.SizeInBytes) { _vertexBuffer.Dispose(); - _vertexBuffer = gd.ResourceFactory.CreateBuffer(new BufferDescription((uint)(totalVBSize * 1.5f), BufferUsage.VertexBuffer | BufferUsage.Dynamic)); + _vertexBuffer = gd.ResourceFactory.CreateBuffer(new BufferDescription((uint)(totalVBSize * 1.5f), + BufferUsage.VertexBuffer | BufferUsage.Dynamic)); + _vertexBuffer.Name = $"ImGui.NET Vertex Buffer"; } uint totalIBSize = (uint)(draw_data.TotalIdxCount * sizeof(ushort)); if (totalIBSize > _indexBuffer.SizeInBytes) { _indexBuffer.Dispose(); - _indexBuffer = gd.ResourceFactory.CreateBuffer(new BufferDescription((uint)(totalIBSize * 1.5f), BufferUsage.IndexBuffer | BufferUsage.Dynamic)); + _indexBuffer = gd.ResourceFactory.CreateBuffer(new BufferDescription((uint)(totalIBSize * 1.5f), + BufferUsage.IndexBuffer | BufferUsage.Dynamic)); + _indexBuffer.Name = $"ImGui.NET Index Buffer"; } for (int i = 0; i < draw_data.CmdListsCount; i++) { - ImDrawListPtr cmd_list = draw_data.CmdListsRange[i]; + ImDrawListPtr cmd_list = draw_data.CmdLists[i]; cl.UpdateBuffer( _vertexBuffer, @@ -576,7 +724,7 @@ private unsafe void RenderImDrawData(ImDrawDataPtr draw_data, GraphicsDevice gd, int idx_offset = 0; for (int n = 0; n < draw_data.CmdListsCount; n++) { - ImDrawListPtr cmd_list = draw_data.CmdListsRange[n]; + ImDrawListPtr cmd_list = draw_data.CmdLists[n]; for (int cmd_i = 0; cmd_i < cmd_list.CmdBuffer.Size; cmd_i++) { ImDrawCmdPtr pcmd = cmd_list.CmdBuffer[cmd_i]; @@ -605,11 +753,12 @@ private unsafe void RenderImDrawData(ImDrawDataPtr draw_data, GraphicsDevice gd, (uint)(pcmd.ClipRect.Z - pcmd.ClipRect.X), (uint)(pcmd.ClipRect.W - pcmd.ClipRect.Y)); - cl.DrawIndexed(pcmd.ElemCount, 1, (uint)idx_offset, vtx_offset, 0); + cl.DrawIndexed(pcmd.ElemCount, 1, pcmd.IdxOffset + (uint)idx_offset, + (int)(pcmd.VtxOffset + vtx_offset), 0); } - - idx_offset += (int)pcmd.ElemCount; } + + idx_offset += cmd_list.IdxBuffer.Size; vtx_offset += cmd_list.VtxBuffer.Size; } }