Create the first immediate mode UI base.

This commit is contained in:
H. Utku Maden 2025-01-25 12:34:37 +03:00
parent ef860f39bf
commit 1cd005f827
8 changed files with 297 additions and 29 deletions

@ -97,7 +97,7 @@ namespace Dashboard.Drawing.OpenGL.Executors
private void DrawText(ICommandFrame frame)
{
TextCommandArgs args = frame.GetParameter<TextCommandArgs>();
DbBlurgFont font = (DbBlurgFont)args.Font;
DbBlurgFont font = Engine.InternFont(args.Font);
BlurgColor color;
switch (args.TextBrush)

@ -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

@ -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)
{

@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Dashboard.Drawing\Dashboard.Drawing.csproj" />
</ItemGroup>
</Project>

@ -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<char> 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:
}
}
}

@ -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

@ -10,6 +10,7 @@
<ItemGroup>
<ProjectReference Include="..\..\Dashboard.Drawing.OpenGL\Dashboard.Drawing.OpenGL.csproj" />
<ProjectReference Include="..\..\Dashboard.Drawing\Dashboard.Drawing.csproj" />
<ProjectReference Include="..\..\Dashboard.ImmediateUI\Dashboard.ImmediateUI.csproj" />
</ItemGroup>
<ItemGroup>

@ -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<Vector3> points = new List<Vector3>();
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);
}