diff --git a/Dashboard.Drawing.OpenGL/Executors/TextCommandExecutor.cs b/Dashboard.Drawing.OpenGL/Executors/TextCommandExecutor.cs index 8b0da71..20dec00 100644 --- a/Dashboard.Drawing.OpenGL/Executors/TextCommandExecutor.cs +++ b/Dashboard.Drawing.OpenGL/Executors/TextCommandExecutor.cs @@ -97,7 +97,7 @@ namespace Dashboard.Drawing.OpenGL.Executors private void DrawText(ICommandFrame frame) { TextCommandArgs args = frame.GetParameter(); - DbBlurgFont font = (DbBlurgFont)args.Font; + DbBlurgFont font = Engine.InternFont(args.Font); BlurgColor color; switch (args.TextBrush) diff --git a/Dashboard.Drawing.OpenGL/Executors/gradient.glsl b/Dashboard.Drawing.OpenGL/Executors/gradient.glsl new file mode 100644 index 0000000..3d16e53 --- /dev/null +++ b/Dashboard.Drawing.OpenGL/Executors/gradient.glsl @@ -0,0 +1,55 @@ +#ifndef _GRADIENT_GLSL_ +#define _GRADIENT_GLSL_ + +#define DB_GRADIENT_MAX 16 + +struct Gradient_t { + float fPosition; + float pad0; + float pad1; + float pad2; + vec4 v4Color; +}; + +uniform GradientBlock +{ + Gradient_t vstGradientStops[DB_GRADIENT_MAX]; +}; + +vec4 getGradientColor(float position, int index, int count) +{ + position = clamp(position, 0, 1); + + int i0 = 0; + float p0 = vstGradientStops[index + i0].fPosition; + + int i1 = count - 1; + float p1 = vstGradientStops[index + i1].fPosition; + + for (int i = 0; i < count; i++) + { + float px = vstGradientStops[index + i].fPosition; + + if (px > p0 && px <= position) + { + p0 = px; + i0 = i; + } + + if (px < p1 && px >= position) + { + p1 = px; + i1 = i; + } + } + + vec4 c0 = vstGradientStops[index + i0].v4Color; + vec4 c1 = vstGradientStops[index + i1].v4Color; + + float l = p1 - p0; + float w = (l > 0) ? (position - p0) / (p1 - p0) : 0; + + return mix(c0, c1, w); +} + +#endif diff --git a/Dashboard.Drawing.OpenGL/Text/BlurgEngine.cs b/Dashboard.Drawing.OpenGL/Text/BlurgEngine.cs index 6f2d091..a3dab4d 100644 --- a/Dashboard.Drawing.OpenGL/Text/BlurgEngine.cs +++ b/Dashboard.Drawing.OpenGL/Text/BlurgEngine.cs @@ -93,7 +93,7 @@ namespace Dashboard.Drawing.OpenGL.Text throw new Exception("Font not found."); } - private DbBlurgFont InternFont(IFont font) + public DbBlurgFont InternFont(IFont font) { if (font is NamedFont named) { diff --git a/Dashboard.ImmediateUI/Dashboard.ImmediateUI.csproj b/Dashboard.ImmediateUI/Dashboard.ImmediateUI.csproj new file mode 100644 index 0000000..70dee33 --- /dev/null +++ b/Dashboard.ImmediateUI/Dashboard.ImmediateUI.csproj @@ -0,0 +1,13 @@ + + + + net8.0 + disable + enable + + + + + + + diff --git a/Dashboard.ImmediateUI/DimUI.cs b/Dashboard.ImmediateUI/DimUI.cs new file mode 100644 index 0000000..f417437 --- /dev/null +++ b/Dashboard.ImmediateUI/DimUI.cs @@ -0,0 +1,157 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Drawing; +using System.Net.Http; +using System.Numerics; +using System.Text; +using Dashboard.Drawing; + +namespace Dashboard.ImmediateUI +{ + public class DimUIConfig + { + public float Margin = 4f; + public float Padding = 4f; + + public required IFont Font { get; init; } + public IBrush TextBrush = new SolidBrush(Color.Black); + + public float ButtonBorderSize = 2f; + public IBrush ButtonBorderBrush = new SolidBrush(Color.DarkSlateGray); + public IBrush ButtonFillBrush = new SolidBrush(Color.SlateGray); + public IBrush? ButtonShadowBrush = new SolidBrush(Color.FromArgb(32, Color.LightSteelBlue)); + public float ButtonShadowOffset = 4f; + + public float InputBorderSize = 2f; + public IBrush InputPlaceholderTextBrush = new SolidBrush(Color.SlateGray); + public IBrush InputBorderBrush = new SolidBrush(Color.SlateGray); + public IBrush InputFillBrush = new SolidBrush(Color.LightGray); + public IBrush? InputShadowBrush = new SolidBrush(Color.FromArgb(32, Color.LightSteelBlue)); + public float InputShadowOffset = -4f; + + public float MenuBorderSize = 2f; + public IBrush MenuBorderBrush = new SolidBrush(Color.DarkSlateGray); + public IBrush MenuFillBrush = new SolidBrush(Color.SlateGray); + public IBrush? MenuShadowBrush = new SolidBrush(Color.FromArgb(32, Color.LightSteelBlue)); + public float MenuShadowOffset = 6f; + } + + public class DimUI + { + private readonly DimUIConfig _config; + private Vector2 _pen; + private Box2d _bounds; + private bool _firstLine = false; + private bool _sameLine = false; + private float _z = -1; + private float _lineHeight; + private DrawQueue _queue; + + public DimUI(DimUIConfig config) + { + _config = config; + } + + [MemberNotNull(nameof(_queue))] + public void Begin(Box2d bounds, DrawQueue queue) + { + _bounds = bounds; + _pen = _bounds.Min; + _queue = queue; + _firstLine = true; + _lineHeight = 0; + _z = -1; + } + + public void SameLine() + { + _sameLine = true; + } + + private void Line() + { + if (!_firstLine && !_sameLine) + { + _pen = new Vector2(_bounds.Left, _pen.Y + _lineHeight); + } + else + { + _firstLine = false; + _sameLine = false; + } + + _pen.X += _config.Margin; + _lineHeight = 0; + } + + private float Z() + { + return _z += 0.001f; + } + + public void Text(string text) + { + Line(); + + SizeF sz = Typesetter.MeasureString(_config.Font, text); + float z = Z(); + float h = _config.Margin * 2 + sz.Height; + _queue.Text(new Vector3(_pen + new Vector2(0, _config.Margin), z), _config.TextBrush, text, _config.Font); + + _lineHeight = Math.Max(_lineHeight, h); + _pen.X += sz.Width; + } + + public bool Button(string label) + { + Line(); + + SizeF sz = Typesetter.MeasureString(_config.Font, label); + + return false; + } + + public bool Input(string placeholder, StringBuilder value) + { + return false; + } + + public void BeginMenu() + { + + } + + public bool MenuItem(string name) + { + return false; + } + + public void EndMenu() + { + + } + + public int Id(ReadOnlySpan str) + { + // Uses the FVN-1A algorithm in 32-bit mode. + const int PRIME = 0x01000193; + const int BASIS = unchecked((int)0x811c9dc5); + + int hash = BASIS; + for (int i = 0; i < str.Length; i++) + { + hash ^= str[i] & 0xFF; + hash *= PRIME; + hash ^= str[i] >> 8; + hash *= PRIME; + } + + return hash; + } + + public void Finish() + { + // TODO: + } + } +} diff --git a/Dashboard.sln b/Dashboard.sln index aeb4144..c718d03 100644 --- a/Dashboard.sln +++ b/Dashboard.sln @@ -19,6 +19,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dashboard.Common", "Dashboa EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dashboard.Drawing.OpenGL", "Dashboard.Drawing.OpenGL\Dashboard.Drawing.OpenGL.csproj", "{454198BA-CB95-41C5-A934-B1C8FDA35A6B}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dashboard.ImmediateUI", "Dashboard.ImmediateUI\Dashboard.ImmediateUI.csproj", "{3F33197F-0B7B-4CD8-98BD-05D6D5EC76B2}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -45,6 +47,10 @@ Global {454198BA-CB95-41C5-A934-B1C8FDA35A6B}.Debug|Any CPU.Build.0 = Debug|Any CPU {454198BA-CB95-41C5-A934-B1C8FDA35A6B}.Release|Any CPU.ActiveCfg = Release|Any CPU {454198BA-CB95-41C5-A934-B1C8FDA35A6B}.Release|Any CPU.Build.0 = Release|Any CPU + {3F33197F-0B7B-4CD8-98BD-05D6D5EC76B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3F33197F-0B7B-4CD8-98BD-05D6D5EC76B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3F33197F-0B7B-4CD8-98BD-05D6D5EC76B2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3F33197F-0B7B-4CD8-98BD-05D6D5EC76B2}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/tests/Dashboard.TestApplication/Dashboard.TestApplication.csproj b/tests/Dashboard.TestApplication/Dashboard.TestApplication.csproj index e7a339c..a9c7e50 100644 --- a/tests/Dashboard.TestApplication/Dashboard.TestApplication.csproj +++ b/tests/Dashboard.TestApplication/Dashboard.TestApplication.csproj @@ -10,6 +10,7 @@ + diff --git a/tests/Dashboard.TestApplication/Program.cs b/tests/Dashboard.TestApplication/Program.cs index a27af4f..794c143 100644 --- a/tests/Dashboard.TestApplication/Program.cs +++ b/tests/Dashboard.TestApplication/Program.cs @@ -3,17 +3,19 @@ using System.Drawing; using Dashboard; using Dashboard.Drawing.OpenGL; using Dashboard.Drawing.OpenGL.Text; +using Dashboard.ImmediateUI; using OpenTK.Graphics; using OpenTK.Platform; using OpenTK.Graphics.OpenGL; using OpenTK.Mathematics; +using Box2d = Dashboard.Box2d; using TK = OpenTK.Platform.Toolkit; using sys = System.Numerics; using otk = OpenTK.Mathematics; TK.Init(new ToolkitOptions() { - ApplicationName = "Paper Punch Out!", + ApplicationName = "DashTerm", Windows = new ToolkitOptions.WindowsOptions() { EnableVisualStyles = true, @@ -39,11 +41,11 @@ WindowHandle wnd = TK.Window.Create(new OpenGLGraphicsApiHints() SupportTransparentFramebufferX11 = true, }); -TK.Window.SetTitle(wnd, "Paper Punch Out!"); +TK.Window.SetTitle(wnd, "DashTerm"); TK.Window.SetMinClientSize(wnd, 300, 200); TK.Window.SetClientSize(wnd, new Vector2i(320, 240)); TK.Window.SetBorderStyle(wnd, WindowBorderStyle.ResizableBorder); -TK.Window.SetTransparencyMode(wnd, WindowTransparencyMode.TransparentFramebuffer, 0.1f); +// TK.Window.SetTransparencyMode(wnd, WindowTransparencyMode.TransparentFramebuffer, 0.1f); OpenGLContextHandle context = TK.OpenGL.CreateFromWindow(wnd); @@ -62,6 +64,10 @@ engine.Initialize(); GlContext dbGlContext = new GlContext(wnd, context); ContextExecutor executor = engine.GetExecutor(dbGlContext); +DimUI dimUI = new DimUI(new DimUIConfig() +{ + Font = new NamedFont("Noto Sans", 9f), +}); Vector2 mousePos = Vector2.Zero; Random r = new Random(); @@ -78,19 +84,6 @@ EventQueue.EventRaised += (handle, type, eventArgs) => case PlatformEventType.MouseMove: mousePos = ((MouseMoveEventArgs)eventArgs).ClientPosition; break; - case PlatformEventType.MouseUp: - MouseButtonUpEventArgs mouseUp = (MouseButtonUpEventArgs)eventArgs; - if (mouseUp.Button == MouseButton.Button1) - { - SolidBrush brush = new SolidBrush(Color.FromArgb(r.Next(256), r.Next(256), r.Next(256), 0)); - queue.Point(new sys::Vector2(mousePos.X, mousePos.Y), 0f, 24f, brush); - } - break; - case PlatformEventType.KeyDown: - KeyDownEventArgs keyDown = (KeyDownEventArgs)eventArgs; - if (keyDown.Key == Key.Escape || keyDown.Key == Key.Delete || keyDown.Key == Key.Backspace) - queue.Clear(); - break; } }; @@ -100,31 +93,74 @@ List points = new List(); IFont font = Typesetter.LoadFont("Nimbus Mono", 12f); -queue.Text(new sys.Vector3(96, 96, 0), bg, "Hello World!", font); -queue.Text(new sys.Vector3(128, 12, 0), bg, "japenis too! uwa ~~~ アホ", font); -queue.Line(new sys.Vector2(64, 256), new sys.Vector2(64+256, 256), 0, 64f, fg); -queue.Rect(new sys.Vector2(16, 16), new sys.Vector2(96, 96), 0, fg, bg, 8f); - while (!shouldExit) { TK.Window.ProcessEvents(true); - TK.Window.GetFramebufferSize(wnd, out Vector2i framebufferSize); executor.BeginFrame(); - // queue.Line(Vector3.Zero, new Vector3(System.Numerics.Vector2.One * 256f, 0), 4f, bg); - // queue.Rect(Vector3.UnitX, 2 * Vector3.UnitX + Vector3.UnitY, fg, bg, 2f); + dimUI.Begin(new Box2d(0, 0, framebufferSize.X, framebufferSize.Y), queue); + dimUI.Text("Hello World!"); + + dimUI.BeginMenu(); + + if (dimUI.MenuItem("File")) + { + dimUI.BeginMenu(); + dimUI.MenuItem("New Window"); + dimUI.MenuItem("Preferences"); + dimUI.MenuItem("Exit"); + dimUI.EndMenu(); + } + + if (dimUI.MenuItem("Edit")) + { + dimUI.BeginMenu(); + dimUI.MenuItem("Cut"); + dimUI.MenuItem("Copy"); + dimUI.MenuItem("Paste"); + + if (dimUI.MenuItem("Send Char")) + { + dimUI.BeginMenu(); + dimUI.EndMenu(); + } + + dimUI.EndMenu(); + } + + if (dimUI.MenuItem("View")) + { + dimUI.BeginMenu(); + dimUI.MenuItem("Clear"); + + if (dimUI.MenuItem("Set Size")) + { + dimUI.BeginMenu(); + dimUI.MenuItem("24 x 40"); + dimUI.MenuItem("25 x 40"); + dimUI.MenuItem("24 x 80"); + dimUI.MenuItem("25 x 80"); + dimUI.MenuItem("25 x 120"); + dimUI.EndMenu(); + } + + dimUI.EndMenu(); + } + + dimUI.Finish(); GL.Viewport(0, 0, framebufferSize.X, framebufferSize.Y); - GL.ClearColor(0.9f, 0.9f, 0.7f, 1.0f); + GL.ClearColor(0.3f, 0.3f, 0.3f, 1.0f); GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); GL.Disable(EnableCap.DepthTest); - // GL.Enable(EnableCap.Blend); - // GL.BlendFuncSeparate(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha, BlendingFactor.One, BlendingFactor.Zero); + GL.Enable(EnableCap.Blend); + GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha); GL.ColorMask(true, true, true, true); executor.Draw(queue); executor.EndFrame(); + queue.Clear(); TK.OpenGL.SwapBuffers(context); }