Rework the library architecture.
This commit is contained in:
parent
66c5eecc26
commit
043060db66
264
Dashboard.BlurgText.OpenGL/BlurgGLExtension.cs
Normal file
264
Dashboard.BlurgText.OpenGL/BlurgGLExtension.cs
Normal file
@ -0,0 +1,264 @@
|
||||
using System.Numerics;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using BlurgText;
|
||||
using Dashboard.Drawing;
|
||||
using Dashboard.OpenGL;
|
||||
using OpenTK.Graphics.OpenGL;
|
||||
using OPENGL = OpenTK.Graphics.OpenGL;
|
||||
|
||||
namespace Dashboard.BlurgText.OpenGL
|
||||
{
|
||||
public class BlurgGLExtension : BlurgDcExtension
|
||||
{
|
||||
private readonly List<int> _textures = new List<int>();
|
||||
private int _program = 0;
|
||||
private int _transformsLocation = -1;
|
||||
private int _atlasLocation = -1;
|
||||
private int _borderWidthLocation = -1;
|
||||
private int _borderColorLocation = -1;
|
||||
private int _fillColorLocation = -1;
|
||||
private int _vertexArray = 0;
|
||||
|
||||
public override Blurg Blurg { get; }
|
||||
public bool SystemFontsEnabled { get; set; }
|
||||
public bool IsDisposed { get; private set; } = false;
|
||||
|
||||
public override string DriverName => "BlurgText";
|
||||
public override string DriverVendor => "Dashboard and BlurgText";
|
||||
public override Version DriverVersion => new Version(1, 0);
|
||||
|
||||
private new GLDeviceContext Context => (GLDeviceContext)base.Context;
|
||||
|
||||
public BlurgGLExtension()
|
||||
{
|
||||
Blurg = new Blurg(AllocateTexture, UpdateTexture);
|
||||
SystemFontsEnabled = Blurg.EnableSystemFonts();
|
||||
}
|
||||
|
||||
~BlurgGLExtension()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
public BlurgFontProxy InternFont(IFont font)
|
||||
{
|
||||
BlurgTextExtension appExtension = Application.ExtensionRequire<BlurgTextExtension>();
|
||||
if (font is FontInfo fontInfo)
|
||||
{
|
||||
return (BlurgFontProxy)appExtension.Load(fontInfo);
|
||||
}
|
||||
else if (font is BlurgFontProxy blurgFont)
|
||||
{
|
||||
if (blurgFont.Owner != Blurg)
|
||||
{
|
||||
if (blurgFont.Path == null)
|
||||
return (BlurgFontProxy)appExtension.Load(new FontInfo(blurgFont.Family, blurgFont.Weight,
|
||||
blurgFont.Slant,
|
||||
blurgFont.Stretch));
|
||||
else
|
||||
return (BlurgFontProxy)appExtension.Load(blurgFont.Path);
|
||||
}
|
||||
else
|
||||
{
|
||||
return blurgFont;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Unsupported font resource.");
|
||||
}
|
||||
}
|
||||
|
||||
private void UseProgram()
|
||||
{
|
||||
if (_program != 0)
|
||||
{
|
||||
GL.UseProgram(_program);
|
||||
return;
|
||||
}
|
||||
|
||||
Assembly self = typeof(BlurgGLExtension).Assembly;
|
||||
|
||||
using Stream vsource = self.GetManifestResourceStream("Dashboard.BlurgText.OpenGL.text.vert")!;
|
||||
using Stream fsource = self.GetManifestResourceStream("Dashboard.BlurgText.OpenGL.text.frag")!;
|
||||
int vs = ShaderUtil.CompileShader(ShaderType.VertexShader, vsource);
|
||||
int fs = ShaderUtil.CompileShader(ShaderType.FragmentShader, fsource);
|
||||
_program = ShaderUtil.LinkProgram(vs, fs, [
|
||||
"a_v3Position",
|
||||
"a_v2TexCoords",
|
||||
]);
|
||||
GL.DeleteShader(vs);
|
||||
GL.DeleteShader(fs);
|
||||
|
||||
_transformsLocation = GL.GetUniformLocation(_program, "m4Transforms");
|
||||
_atlasLocation = GL.GetUniformLocation(_program, "txAtlas");
|
||||
_borderWidthLocation = GL.GetUniformLocation(_program, "fBorderWidth");
|
||||
_borderColorLocation = GL.GetUniformLocation(_program, "v4BorderColor");
|
||||
_fillColorLocation = GL.GetUniformLocation(_program, "v4FillColor");
|
||||
|
||||
GL.UseProgram(_program);
|
||||
GL.Uniform1i(_atlasLocation, 0);
|
||||
}
|
||||
|
||||
private void UpdateTexture(IntPtr texture, IntPtr buffer, int x, int y, int width, int height)
|
||||
{
|
||||
GL.BindTexture(TextureTarget.Texture2d, (int)texture);
|
||||
GL.TexSubImage2D(TextureTarget.Texture2d, 0, x, y, width, height, OPENGL.PixelFormat.Rgba, PixelType.UnsignedByte, buffer);
|
||||
// GL.TexSubImage2D(TextureTarget.Texture2d, 0, x, y, width, height, OPENGL.PixelFormat.Red, PixelType.Byte, buffer);
|
||||
}
|
||||
|
||||
|
||||
private IntPtr AllocateTexture(int width, int height)
|
||||
{
|
||||
int texture = GL.GenTexture();
|
||||
|
||||
GL.BindTexture(TextureTarget.Texture2d, texture);
|
||||
GL.TexImage2D(TextureTarget.Texture2d, 0, InternalFormat.Rgba, width, height, 0, OPENGL.PixelFormat.Rgba, PixelType.UnsignedByte, IntPtr.Zero);
|
||||
// GL.TexImage2D(TextureTarget.Texture2d, 0, InternalFormat.R8, width, height, 0, OPENGL.PixelFormat.Red, PixelType.Byte, IntPtr.Zero);
|
||||
|
||||
GL.TexParameteri(TextureTarget.Texture2d, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
|
||||
GL.TexParameteri(TextureTarget.Texture2d, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
|
||||
// GL.TexParameteri(TextureTarget.Texture2d, TextureParameterName.TextureSwizzleR, (int)TextureSwizzle.One);
|
||||
// GL.TexParameteri(TextureTarget.Texture2d, TextureParameterName.TextureSwizzleG, (int)TextureSwizzle.One);
|
||||
// GL.TexParameteri(TextureTarget.Texture2d, TextureParameterName.TextureSwizzleB, (int)TextureSwizzle.One);
|
||||
// GL.TexParameteri(TextureTarget.Texture2d, TextureParameterName.TextureSwizzleA, (int)TextureSwizzle.Red);
|
||||
|
||||
_textures.Add(texture);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (IsDisposed)
|
||||
return;
|
||||
IsDisposed = true;
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
GC.SuppressFinalize(this);
|
||||
foreach (int texture in _textures)
|
||||
{
|
||||
Context.Collector.DeleteTexture(texture);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void DrawBlurgResult(BlurgResult result, Vector3 position)
|
||||
{
|
||||
if (result.Count == 0)
|
||||
return;
|
||||
|
||||
Matrix4x4 view = Matrix4x4.CreateTranslation(position) * Context.ExtensionRequire<IDeviceContextBase>().Transforms;
|
||||
|
||||
List<DrawCall> drawCalls = new List<DrawCall>();
|
||||
List<Vertex> vertices = new List<Vertex>();
|
||||
List<ushort> indices = new List<ushort>();
|
||||
int offset = 0;
|
||||
int count = 0;
|
||||
|
||||
DrawCall prototype = default;
|
||||
for (int i = 0; i < result.Count; i++)
|
||||
{
|
||||
BlurgRect rect = result[i];
|
||||
|
||||
int texture = (int)rect.UserData;
|
||||
Vector4 fillColor = new Vector4(rect.Color.R / 255f, rect.Color.G / 255f, rect.Color.B / 255f,
|
||||
rect.Color.A / 255f);
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
prototype = new DrawCall(0, 0, texture, fillColor);
|
||||
}
|
||||
else if (prototype.Texture != texture || prototype.FillColor != fillColor)
|
||||
{
|
||||
drawCalls.Add(prototype with { Count = count, Offset = offset });
|
||||
prototype = new DrawCall(0, 0, texture, fillColor);
|
||||
offset += count;
|
||||
count = 0;
|
||||
}
|
||||
|
||||
vertices.Add(new Vertex(rect.X, rect.Y, 0, rect.U0, rect.V0));
|
||||
vertices.Add(new Vertex(rect.X + rect.Width, rect.Y, 0, rect.U1, rect.V0));
|
||||
vertices.Add(new Vertex(rect.X + rect.Width, rect.Y + rect.Height, 0, rect.U1, rect.V1));
|
||||
vertices.Add(new Vertex(rect.X, rect.Y + rect.Height, 0, rect.U0, rect.V1));
|
||||
|
||||
indices.Add((ushort)(vertices.Count - 4));
|
||||
indices.Add((ushort)(vertices.Count - 3));
|
||||
indices.Add((ushort)(vertices.Count - 2));
|
||||
indices.Add((ushort)(vertices.Count - 4));
|
||||
indices.Add((ushort)(vertices.Count - 2));
|
||||
indices.Add((ushort)(vertices.Count - 1));
|
||||
|
||||
count += 6;
|
||||
}
|
||||
drawCalls.Add(prototype with { Count = count, Offset = offset });
|
||||
|
||||
if (_vertexArray == 0)
|
||||
{
|
||||
_vertexArray = GL.GenVertexArray();
|
||||
}
|
||||
GL.BindVertexArray(_vertexArray);
|
||||
|
||||
Span<int> buffers = stackalloc int[2];
|
||||
GL.GenBuffers(2, buffers);
|
||||
GL.BindBuffer(BufferTarget.ArrayBuffer, buffers[0]);
|
||||
GL.BufferData(BufferTarget.ArrayBuffer, vertices.Count * Vertex.Size, (ReadOnlySpan<Vertex>)CollectionsMarshal.AsSpan(vertices), BufferUsage.StaticRead);
|
||||
|
||||
GL.BindBuffer(BufferTarget.ElementArrayBuffer, buffers[1]);
|
||||
GL.BufferData(BufferTarget.ElementArrayBuffer, indices.Count * sizeof(ushort), (ReadOnlySpan<ushort>)CollectionsMarshal.AsSpan(indices), BufferUsage.StaticRead);
|
||||
|
||||
GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, Vertex.Size, Vertex.PositionOffset);
|
||||
GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, Vertex.Size, Vertex.TexCoordOffset);
|
||||
GL.EnableVertexAttribArray(0); GL.EnableVertexAttribArray(1);
|
||||
|
||||
UseProgram();
|
||||
|
||||
GL.UniformMatrix4f(_transformsLocation, 1, true, in view);
|
||||
GL.ActiveTexture(TextureUnit.Texture0);
|
||||
|
||||
foreach (DrawCall call in drawCalls)
|
||||
{
|
||||
GL.BindTexture(TextureTarget.Texture2d, call.Texture);
|
||||
GL.Uniform4f(_fillColorLocation, call.FillColor.X, call.FillColor.Y, call.FillColor.Z,
|
||||
call.FillColor.W);
|
||||
GL.DrawElements(PrimitiveType.Triangles, call.Count, DrawElementsType.UnsignedShort, call.Offset);
|
||||
}
|
||||
|
||||
GL.DeleteBuffers(2, buffers);
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit, Size = Size)]
|
||||
private struct Vertex(Vector3 position, Vector2 texCoord)
|
||||
{
|
||||
[FieldOffset(PositionOffset)]
|
||||
public Vector3 Position = position;
|
||||
|
||||
[FieldOffset(TexCoordOffset)]
|
||||
public Vector2 TexCoord = texCoord;
|
||||
|
||||
public Vertex(float x, float y, float z, float u, float v)
|
||||
: this(new Vector3(x, y, z), new Vector2(u, v))
|
||||
{
|
||||
}
|
||||
|
||||
public const int Size = 8 * sizeof(float);
|
||||
public const int PositionOffset = 0 * sizeof(float);
|
||||
public const int TexCoordOffset = 4 * sizeof(float);
|
||||
}
|
||||
|
||||
private struct DrawCall(int offset, int count, int texture, Vector4 fillColor)
|
||||
{
|
||||
public int Offset = offset;
|
||||
public int Count = count;
|
||||
public int Texture = texture;
|
||||
public Vector4 FillColor = fillColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
20
Dashboard.BlurgText.OpenGL/Dashboard.BlurgText.OpenGL.csproj
Normal file
20
Dashboard.BlurgText.OpenGL/Dashboard.BlurgText.OpenGL.csproj
Normal file
@ -0,0 +1,20 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../Dashboard.BlurgText/Dashboard.BlurgText.csproj"/>
|
||||
<ProjectReference Include="..\Dashboard.OpenGL\Dashboard.OpenGL.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="text.frag" />
|
||||
<EmbeddedResource Include="text.vert" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
21
Dashboard.BlurgText.OpenGL/text.frag
Normal file
21
Dashboard.BlurgText.OpenGL/text.frag
Normal file
@ -0,0 +1,21 @@
|
||||
#version 140
|
||||
|
||||
in vec3 v_v3Position;
|
||||
in vec2 v_v2TexCoords;
|
||||
|
||||
out vec4 f_Color;
|
||||
|
||||
uniform sampler2D txAtlas;
|
||||
uniform float fBorderWidth;
|
||||
uniform vec4 v4BorderColor;
|
||||
uniform vec4 v4FillColor;
|
||||
|
||||
void main() {
|
||||
// For now just honor the fill color
|
||||
|
||||
vec4 color = texture(txAtlas, v_v2TexCoords) * v4FillColor;
|
||||
|
||||
if (color.a <= 0.01)
|
||||
discard;
|
||||
f_Color = color;
|
||||
}
|
||||
18
Dashboard.BlurgText.OpenGL/text.vert
Normal file
18
Dashboard.BlurgText.OpenGL/text.vert
Normal file
@ -0,0 +1,18 @@
|
||||
#version 140
|
||||
|
||||
in vec3 a_v3Position;
|
||||
in vec2 a_v2TexCoords;
|
||||
|
||||
out vec3 v_v3Position;
|
||||
out vec2 v_v2TexCoords;
|
||||
|
||||
uniform mat4 m4Transforms;
|
||||
|
||||
void main(void)
|
||||
{
|
||||
vec4 position = vec4(a_v3Position, 1) * m4Transforms;
|
||||
gl_Position = position;
|
||||
v_v3Position = position.xyz/position.w;
|
||||
|
||||
v_v2TexCoords = a_v2TexCoords;
|
||||
}
|
||||
21
Dashboard.BlurgText/BlurgFontProxy.cs
Normal file
21
Dashboard.BlurgText/BlurgFontProxy.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using BlurgText;
|
||||
using Dashboard.Drawing;
|
||||
|
||||
namespace Dashboard.BlurgText
|
||||
{
|
||||
public class BlurgFontProxy(Blurg owner, BlurgFont font) : IFont
|
||||
{
|
||||
public Blurg Owner { get; } = owner;
|
||||
public BlurgFont Font { get; } = font;
|
||||
public string Family => Font.FamilyName;
|
||||
public FontWeight Weight => (FontWeight)Font.Weight.Value;
|
||||
public FontSlant Slant => Font.Italic ? FontSlant.Italic : FontSlant.Normal;
|
||||
public FontStretch Stretch => FontStretch.Normal;
|
||||
|
||||
public string? Path { get; init; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
165
Dashboard.BlurgText/BlurgTextExtension.cs
Normal file
165
Dashboard.BlurgText/BlurgTextExtension.cs
Normal file
@ -0,0 +1,165 @@
|
||||
using System.Diagnostics;
|
||||
using System.Numerics;
|
||||
using BlurgText;
|
||||
using Dashboard.Drawing;
|
||||
using Dashboard.Pal;
|
||||
|
||||
namespace Dashboard.BlurgText
|
||||
{
|
||||
public interface IBlurgDcExtensionFactory
|
||||
{
|
||||
public BlurgDcExtension CreateExtension(BlurgTextExtension appExtension, DeviceContext dc);
|
||||
}
|
||||
|
||||
public class BlurgTextExtension(IBlurgDcExtensionFactory dcExtensionFactory) : IApplicationExtension, IFontLoader
|
||||
{
|
||||
private readonly Blurg _blurg = new Blurg(GlobalTextureAllocation, GlobalTextureUpdate);
|
||||
|
||||
public Application Context { get; private set; } = null!;
|
||||
public string DriverName { get; } = "BlurgText";
|
||||
public string DriverVendor { get; } = "Dashbord and BlurgText";
|
||||
public Version DriverVersion { get; } = new Version(1, 0);
|
||||
public IBlurgDcExtensionFactory DcExtensionFactory { get; } = dcExtensionFactory;
|
||||
IContextBase IContextExtensionBase.Context => Context;
|
||||
public bool IsDisposed { get; private set; } = false;
|
||||
|
||||
public void Require(Application context)
|
||||
{
|
||||
Context = context;
|
||||
context.DeviceContextCreated += OnDeviceContextCreated;
|
||||
}
|
||||
|
||||
void IContextExtensionBase.Require(IContextBase context) => Require((Application)context);
|
||||
|
||||
private void RequireDeviceContextExtension(DeviceContext dc)
|
||||
{
|
||||
dc.ExtensionPreload<BlurgDcExtension>(() => DcExtensionFactory.CreateExtension(this, dc));
|
||||
}
|
||||
|
||||
private void OnDeviceContextCreated(object? sender, DeviceContext dc)
|
||||
{
|
||||
RequireDeviceContextExtension(dc);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (IsDisposed)
|
||||
return;
|
||||
IsDisposed = true;
|
||||
|
||||
_blurg.Dispose();
|
||||
Context.DeviceContextCreated -= OnDeviceContextCreated;
|
||||
}
|
||||
|
||||
private static void GlobalTextureUpdate(IntPtr userdata, IntPtr buffer, int x, int y, int width, int height)
|
||||
{
|
||||
// Report the user error.
|
||||
Debug.WriteLine("Attempt to create or update a texture from the global BlurgText.", "Dashboard/BlurgEngine");
|
||||
}
|
||||
|
||||
private static IntPtr GlobalTextureAllocation(int width, int height)
|
||||
{
|
||||
Debug.WriteLine("Attempt to create or update a texture from the global BlurgText.", "Dashboard/BlurgEngine");
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
public IFont Load(FontInfo info)
|
||||
{
|
||||
BlurgFont? font = _blurg.QueryFont(
|
||||
info.Family,
|
||||
info.Weight switch
|
||||
{
|
||||
FontWeight._100 => global::BlurgText.FontWeight.Thin,
|
||||
FontWeight._200 => global::BlurgText.FontWeight.ExtraLight,
|
||||
FontWeight._300 => global::BlurgText.FontWeight.Light,
|
||||
FontWeight._500 => global::BlurgText.FontWeight.Medium,
|
||||
FontWeight._600 => global::BlurgText.FontWeight.SemiBold,
|
||||
FontWeight._700 => global::BlurgText.FontWeight.Bold,
|
||||
FontWeight._800 => global::BlurgText.FontWeight.ExtraBold,
|
||||
FontWeight._900 => global::BlurgText.FontWeight.Black,
|
||||
_ => global::BlurgText.FontWeight.Regular,
|
||||
},
|
||||
info.Slant switch
|
||||
{
|
||||
FontSlant.Oblique or FontSlant.Italic => true,
|
||||
_ => false,
|
||||
});
|
||||
|
||||
if (font == null)
|
||||
throw new Exception("Font not found.");
|
||||
|
||||
return new BlurgFontProxy(_blurg, font);
|
||||
}
|
||||
|
||||
public IFont Load(string path)
|
||||
{
|
||||
BlurgFont? font = _blurg.AddFontFile(path);
|
||||
|
||||
if (font == null)
|
||||
throw new Exception("Font not found.");
|
||||
|
||||
return new BlurgFontProxy(_blurg, font);
|
||||
}
|
||||
|
||||
public IFont Load(Stream stream)
|
||||
{
|
||||
string path;
|
||||
Stream dest;
|
||||
for (int i = 0;; i++)
|
||||
{
|
||||
path = Path.GetTempFileName();
|
||||
try
|
||||
{
|
||||
dest = File.Open(path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
if (i < 3)
|
||||
continue;
|
||||
else
|
||||
throw new Exception("Could not open a temporary file for writing the font.", ex);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
stream.CopyTo(dest);
|
||||
dest.Dispose();
|
||||
|
||||
return Load(path);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class BlurgDcExtension : IDeviceContextExtension
|
||||
{
|
||||
public abstract Blurg Blurg { get; }
|
||||
public abstract string DriverName { get; }
|
||||
public abstract string DriverVendor { get; }
|
||||
public abstract Version DriverVersion { get; }
|
||||
|
||||
public Application Application => Context.Application;
|
||||
public DeviceContext Context { get; private set; } = null!;
|
||||
|
||||
public abstract void DrawBlurgResult(BlurgResult result, Vector3 position);
|
||||
|
||||
public void DrawBlurgFormattedText(BlurgFormattedText text, Vector3 position, float width = 0f)
|
||||
{
|
||||
BlurgResult? result = Blurg.BuildFormattedText(text, maxWidth: width);
|
||||
if (result == null)
|
||||
throw new Exception("Could not build formatted text result.");
|
||||
|
||||
DrawBlurgResult(result, position);
|
||||
}
|
||||
|
||||
public abstract void Dispose();
|
||||
|
||||
IContextBase IContextExtensionBase.Context => Context;
|
||||
|
||||
public void Require(DeviceContext context)
|
||||
{
|
||||
Context = context;
|
||||
}
|
||||
|
||||
void IContextExtensionBase.Require(IContextBase context) => Require((DeviceContext)context);
|
||||
}
|
||||
}
|
||||
15
Dashboard.BlurgText/Dashboard.BlurgText.csproj
Normal file
15
Dashboard.BlurgText/Dashboard.BlurgText.csproj
Normal file
@ -0,0 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BlurgText" Version="0.1.0-nightly-19" />
|
||||
<ProjectReference Include="..\Dashboard.Common\Dashboard.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
20
Dashboard.Common/Drawing/IDeviceContextBase.cs
Normal file
20
Dashboard.Common/Drawing/IDeviceContextBase.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using System.Drawing;
|
||||
using System.Numerics;
|
||||
using Dashboard.Pal;
|
||||
|
||||
namespace Dashboard.Drawing
|
||||
{
|
||||
public interface IDeviceContextBase : IDeviceContextExtension
|
||||
{
|
||||
RectangleF ClipRegion { get; }
|
||||
Matrix4x4 Transforms { get; }
|
||||
|
||||
void ResetClip();
|
||||
void PushClip(RectangleF clipRegion);
|
||||
void PopClip();
|
||||
|
||||
void ResetTransforms();
|
||||
void PushTransforms(in Matrix4x4 matrix);
|
||||
void PopTransforms();
|
||||
}
|
||||
}
|
||||
25
Dashboard.Common/Drawing/IFontLoader.cs
Normal file
25
Dashboard.Common/Drawing/IFontLoader.cs
Normal file
@ -0,0 +1,25 @@
|
||||
namespace Dashboard.Drawing
|
||||
{
|
||||
public interface IFont : IDisposable
|
||||
{
|
||||
string Family { get; }
|
||||
FontWeight Weight { get; }
|
||||
FontSlant Slant { get; }
|
||||
FontStretch Stretch { get; }
|
||||
}
|
||||
|
||||
public record struct FontInfo(string Family, FontWeight Weight = FontWeight.Normal,
|
||||
FontSlant Slant = FontSlant.Normal, FontStretch Stretch = FontStretch.Normal) : IFont
|
||||
{
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public interface IFontLoader
|
||||
{
|
||||
IFont Load(FontInfo info);
|
||||
IFont Load(string path);
|
||||
IFont Load(Stream stream);
|
||||
}
|
||||
}
|
||||
@ -8,6 +8,8 @@ namespace Dashboard.Drawing
|
||||
{
|
||||
void ClearColor(Color color);
|
||||
|
||||
|
||||
|
||||
void Line(Vector2 a, Vector2 b, float width, float depth, Vector4 color);
|
||||
void Rectangle(Box2d rectangle, float depth, Vector4 color);
|
||||
void Image(Box2d rectangle, Box2d uv, float depth, ITexture texture);
|
||||
|
||||
12
Dashboard.Common/Drawing/ITextRenderer.cs
Normal file
12
Dashboard.Common/Drawing/ITextRenderer.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using System.Numerics;
|
||||
using Dashboard.Pal;
|
||||
|
||||
namespace Dashboard.Drawing
|
||||
{
|
||||
|
||||
public interface ITextRenderer : IDeviceContextExtension
|
||||
{
|
||||
Box2d MeasureText(IFont font, float size, string text);
|
||||
void DrawText(Vector2 position, Vector4 color, float size, IFont font, string text);
|
||||
}
|
||||
}
|
||||
@ -1,20 +0,0 @@
|
||||
namespace Dashboard.Events
|
||||
{
|
||||
public class AnimationTickEventArgs : UiEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Animation delta time in seconds.
|
||||
/// </summary>
|
||||
public float Delta { get; }
|
||||
|
||||
public AnimationTickEventArgs(float delta) : base(UiEventType.AnimationTick)
|
||||
{
|
||||
Delta = delta;
|
||||
}
|
||||
}
|
||||
|
||||
public interface IAnimationTickEvent
|
||||
{
|
||||
event EventHandler<AnimationTickEventArgs> AnimationTimerEvent;
|
||||
}
|
||||
}
|
||||
47
Dashboard.Common/Events/KeyboardEvents.cs
Normal file
47
Dashboard.Common/Events/KeyboardEvents.cs
Normal file
@ -0,0 +1,47 @@
|
||||
namespace Dashboard.Events
|
||||
{
|
||||
[Flags]
|
||||
public enum ModifierKeys
|
||||
{
|
||||
None = 0,
|
||||
LeftBitPos = 8,
|
||||
RightBitPos = 16,
|
||||
|
||||
Shift = (1 << 0),
|
||||
Control = (1 << 1),
|
||||
Alt = (1 << 2),
|
||||
Meta = (1 << 3),
|
||||
|
||||
NumLock = (1 << 4),
|
||||
CapsLock = (1 << 5),
|
||||
ScrollLock = (1 << 6),
|
||||
|
||||
LeftShift = (Shift << LeftBitPos),
|
||||
LeftControl = (Control << LeftBitPos),
|
||||
LeftAlt = (Alt << LeftBitPos),
|
||||
LeftMeta = (Meta << LeftBitPos),
|
||||
|
||||
RightShift = (Shift << RightBitPos),
|
||||
RightControl = (Control << RightBitPos),
|
||||
RightAlt = (Alt << RightBitPos),
|
||||
RightMeta = (Meta << RightBitPos),
|
||||
}
|
||||
|
||||
public enum KeyCode
|
||||
{
|
||||
// TODO:
|
||||
}
|
||||
|
||||
public enum ScanCode
|
||||
{
|
||||
// TODO:
|
||||
}
|
||||
|
||||
public class KeyboardButtonEventArgs(KeyCode keyCode, ScanCode scanCode, ModifierKeys modifierKeys, bool up)
|
||||
: UiEventArgs(up ? UiEventType.KeyUp : UiEventType.KeyDown)
|
||||
{
|
||||
public KeyCode KeyCode { get; } = keyCode;
|
||||
public ScanCode ScanCode { get; } = scanCode;
|
||||
public ModifierKeys ModifierKeys { get; } = modifierKeys;
|
||||
}
|
||||
}
|
||||
@ -20,50 +20,24 @@ namespace Dashboard.Events
|
||||
Middle = M3,
|
||||
}
|
||||
|
||||
public sealed class MouseMoveEventArgs : UiEventArgs
|
||||
public sealed class MouseMoveEventArgs(Vector2 clientPosition, Vector2 delta) : UiEventArgs(UiEventType.MouseMove)
|
||||
{
|
||||
public Vector2 ClientPosition { get; }
|
||||
public Vector2 Delta { get; }
|
||||
|
||||
public MouseMoveEventArgs(Vector2 clientPosition, Vector2 delta)
|
||||
: base(UiEventType.MouseMove)
|
||||
{
|
||||
ClientPosition = clientPosition;
|
||||
Delta = delta;
|
||||
}
|
||||
public Vector2 ClientPosition { get; } = clientPosition;
|
||||
public Vector2 Delta { get; } = delta;
|
||||
}
|
||||
|
||||
public sealed class MouseButtonEventArgs : UiEventArgs
|
||||
public sealed class MouseButtonEventArgs(Vector2 clientPosition, MouseButtons buttons, ModifierKeys modifierKeys, bool up)
|
||||
: UiEventArgs(up ? UiEventType.MouseButtonUp : UiEventType.MouseButtonDown)
|
||||
{
|
||||
public Vector2 ClientPosition { get; }
|
||||
public MouseButtons Buttons { get; }
|
||||
|
||||
public MouseButtonEventArgs(Vector2 clientPosition, MouseButtons buttons, bool up)
|
||||
: base(up ? UiEventType.MouseButtonUp : UiEventType.MouseButtonDown)
|
||||
{
|
||||
ClientPosition = clientPosition;
|
||||
Buttons = buttons;
|
||||
}
|
||||
public ModifierKeys ModifierKeys { get; } = modifierKeys;
|
||||
public Vector2 ClientPosition { get; } = clientPosition;
|
||||
public MouseButtons Buttons { get; } = buttons;
|
||||
}
|
||||
|
||||
public sealed class MouseScrollEventArgs : UiEventArgs
|
||||
public sealed class MouseScrollEventArgs(Vector2 clientPosition, Vector2 scrollDelta)
|
||||
: UiEventArgs(UiEventType.MouseScroll)
|
||||
{
|
||||
public Vector2 ClientPosition { get; }
|
||||
public Vector2 ScrollDelta { get; }
|
||||
|
||||
public MouseScrollEventArgs(Vector2 clientPosition, Vector2 scrollDelta)
|
||||
: base(UiEventType.MouseScroll)
|
||||
{
|
||||
ClientPosition = clientPosition;
|
||||
ScrollDelta = scrollDelta;
|
||||
}
|
||||
}
|
||||
|
||||
public interface IMouseEvents
|
||||
{
|
||||
event EventHandler<MouseMoveEventArgs> MouseMoved;
|
||||
event EventHandler<MouseButtonEventArgs> MouseButtonDown;
|
||||
event EventHandler<MouseButtonEventArgs> MouseButtonUp;
|
||||
event EventHandler<MouseScrollEventArgs> MouseScroll;
|
||||
public Vector2 ClientPosition { get; } = clientPosition;
|
||||
public Vector2 ScrollDelta { get; } = scrollDelta;
|
||||
}
|
||||
}
|
||||
|
||||
15
Dashboard.Common/Events/TickEventArgs.cs
Normal file
15
Dashboard.Common/Events/TickEventArgs.cs
Normal file
@ -0,0 +1,15 @@
|
||||
namespace Dashboard.Events
|
||||
{
|
||||
public class TickEventArgs : UiEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Animation delta time in seconds.
|
||||
/// </summary>
|
||||
public float Delta { get; }
|
||||
|
||||
public TickEventArgs(float delta) : base(UiEventType.AnimationTick)
|
||||
{
|
||||
Delta = delta;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
using System.Numerics;
|
||||
using Dashboard.Pal;
|
||||
|
||||
namespace Dashboard.Events
|
||||
{
|
||||
@ -6,6 +7,7 @@ namespace Dashboard.Events
|
||||
{
|
||||
None,
|
||||
AnimationTick, // Generic timer event.
|
||||
Paint, // Generic paint event.
|
||||
|
||||
// Text input related events.
|
||||
KeyDown, // Keyboard key down.
|
||||
@ -57,6 +59,11 @@ namespace Dashboard.Events
|
||||
public static readonly UiEventArgs None = new UiEventArgs(UiEventType.None);
|
||||
}
|
||||
|
||||
public class PaintEventArgs(DeviceContext dc) : UiEventArgs(UiEventType.Paint)
|
||||
{
|
||||
public DeviceContext DeviceContext { get; } = dc;
|
||||
}
|
||||
|
||||
public class ControlMovedEventArgs : UiEventArgs
|
||||
{
|
||||
public Vector2 OldPosition { get; }
|
||||
|
||||
7
Dashboard.Common/Events/WindowEvent.cs
Normal file
7
Dashboard.Common/Events/WindowEvent.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace Dashboard.Events
|
||||
{
|
||||
public class WindowCloseEvent() : UiEventArgs(UiEventType.WindowClose)
|
||||
{
|
||||
public bool Cancel { get; set; } = false;
|
||||
}
|
||||
}
|
||||
@ -8,6 +8,7 @@ namespace Dashboard
|
||||
_400 = 400,
|
||||
_500 = 500,
|
||||
_600 = 600,
|
||||
_700 = 700,
|
||||
_800 = 800,
|
||||
_900 = 900,
|
||||
|
||||
|
||||
@ -21,6 +21,8 @@ namespace Dashboard.Pal
|
||||
private readonly TypeDictionary<IApplicationExtension, Func<IApplicationExtension>> _preloadedExtensions =
|
||||
new TypeDictionary<IApplicationExtension, Func<IApplicationExtension>>(true);
|
||||
|
||||
public event EventHandler<DeviceContext>? DeviceContextCreated;
|
||||
|
||||
public Application()
|
||||
{
|
||||
Current = this;
|
||||
@ -45,6 +47,11 @@ namespace Dashboard.Pal
|
||||
{
|
||||
}
|
||||
|
||||
protected internal virtual void OnDeviceContextCreated(DeviceContext dc)
|
||||
{
|
||||
DeviceContextCreated?.Invoke(this, dc);
|
||||
}
|
||||
|
||||
public virtual void RunEvents(bool wait)
|
||||
{
|
||||
if (!IsInitialized)
|
||||
@ -152,6 +159,7 @@ namespace Dashboard.Pal
|
||||
return false;
|
||||
|
||||
_extensions.Add(instance);
|
||||
instance.Require(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using Dashboard.Collections;
|
||||
using Dashboard.Windowing;
|
||||
using BindingFlags = System.Reflection.BindingFlags;
|
||||
|
||||
namespace Dashboard.Pal
|
||||
@ -13,6 +14,8 @@ namespace Dashboard.Pal
|
||||
private readonly Dictionary<string, object> _attributes = new Dictionary<string, object>();
|
||||
|
||||
|
||||
public Application Application { get; }
|
||||
public IWindow? Window { get; }
|
||||
public abstract string DriverName { get; }
|
||||
public abstract string DriverVendor { get; }
|
||||
public abstract Version DriverVersion { get; }
|
||||
@ -24,6 +27,13 @@ namespace Dashboard.Pal
|
||||
/// </summary>
|
||||
public IContextDebugger? Debugger { get; set; }
|
||||
|
||||
protected DeviceContext(Application app, IWindow? window)
|
||||
{
|
||||
Application = app;
|
||||
Window = window;
|
||||
app.OnDeviceContextCreated(this);
|
||||
}
|
||||
|
||||
~DeviceContext()
|
||||
{
|
||||
Dispose(false);
|
||||
|
||||
@ -11,7 +11,7 @@ namespace Dashboard.Windowing
|
||||
/// <summary>
|
||||
/// Interface for classes that implement a window manager.
|
||||
/// </summary>
|
||||
public interface IWindowManager : IEnumerable<IVirtualWindow>, IEventListener, IPaintable, IDisposable
|
||||
public interface IWindowManager : IEnumerable<IVirtualWindow>, IEventListener
|
||||
{
|
||||
/// <summary>
|
||||
/// The physical window that this window manager is associated with.
|
||||
|
||||
@ -2,10 +2,13 @@ namespace Dashboard.Windowing
|
||||
{
|
||||
public interface IEventListener
|
||||
{
|
||||
event EventHandler? EventRaised;
|
||||
|
||||
/// <summary>
|
||||
/// Send an event to this windowing object.
|
||||
/// </summary>
|
||||
/// <param name="sender">The object which generated the event.</param>
|
||||
/// <param name="args">The event arguments sent.</param>
|
||||
void SendEvent(EventArgs args);
|
||||
void SendEvent(object? sender, EventArgs args);
|
||||
}
|
||||
}
|
||||
|
||||
8
Dashboard.Common/Windowing/IForm.cs
Normal file
8
Dashboard.Common/Windowing/IForm.cs
Normal file
@ -0,0 +1,8 @@
|
||||
namespace Dashboard.Windowing
|
||||
{
|
||||
public interface IForm : IEventListener, IDisposable
|
||||
{
|
||||
public IWindow Window { get; }
|
||||
public string Title { get; }
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,4 @@
|
||||
using System.Drawing;
|
||||
using Dashboard.Events;
|
||||
using Dashboard.Pal;
|
||||
|
||||
namespace Dashboard.Windowing
|
||||
@ -7,12 +6,13 @@ namespace Dashboard.Windowing
|
||||
/// <summary>
|
||||
/// Base class of all Dashboard windows.
|
||||
/// </summary>
|
||||
public interface IWindow :
|
||||
IPaintable,
|
||||
IDisposable,
|
||||
IAnimationTickEvent,
|
||||
IMouseEvents
|
||||
public interface IWindow : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// The application for this window.
|
||||
/// </summary>
|
||||
Application Application { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Name of the window.
|
||||
/// </summary>
|
||||
@ -27,6 +27,23 @@ namespace Dashboard.Windowing
|
||||
/// The size of the window that excludes the window extents.
|
||||
/// </summary>
|
||||
SizeF ClientSize { get; set; }
|
||||
|
||||
IForm? Form { get; set; }
|
||||
|
||||
public event EventHandler? EventRaised;
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe to events from this window.
|
||||
/// </summary>
|
||||
/// <param name="listener">The event listener instance.</param>
|
||||
/// <returns>An unsubscription token.</returns>
|
||||
public void SubcribeEvent(IEventListener listener);
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribe from events in from this window.
|
||||
/// </summary>
|
||||
/// <param name="listener">The event listener to unsubscribe.</param>
|
||||
public void UnsubscribeEvent(IEventListener listener);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -23,4 +23,8 @@
|
||||
<EmbeddedResource Include="Executors\text.frag" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Text\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
using System.Drawing;
|
||||
using OpenTK.Graphics.OpenGL;
|
||||
using System.Numerics;
|
||||
using Dashboard.OpenGL;
|
||||
using OTK = OpenTK.Mathematics;
|
||||
|
||||
namespace Dashboard.Drawing.OpenGL.Executors
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using BlurgText;
|
||||
using Dashboard.Drawing.OpenGL.Text;
|
||||
using Dashboard.OpenGL;
|
||||
using OpenTK.Graphics.OpenGL;
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
@ -11,7 +11,7 @@ namespace Dashboard.Drawing.OpenGL.Executors
|
||||
{
|
||||
public IEnumerable<string> Extensions { get; } = new[] { "DB_Text" };
|
||||
public IContextExecutor Executor { get; private set; }
|
||||
private BlurgEngine Engine => Executor.ResourcePool.GetResourceManager<BlurgEngine>();
|
||||
// private BlurgEngine Engine => Executor.ResourcePool.GetResourceManager<BlurgEngine>();
|
||||
public bool IsInitialized { get; private set; }
|
||||
|
||||
private DrawCallRecorder _recorder;
|
||||
@ -97,7 +97,7 @@ namespace Dashboard.Drawing.OpenGL.Executors
|
||||
private void DrawText(ICommandFrame frame)
|
||||
{
|
||||
TextCommandArgs args = frame.GetParameter<TextCommandArgs>();
|
||||
DbBlurgFont font = Engine.InternFont(args.Font);
|
||||
// DbBlurgFont font = Engine.InternFont(args.Font);
|
||||
|
||||
BlurgColor color;
|
||||
switch (args.TextBrush)
|
||||
@ -116,15 +116,15 @@ namespace Dashboard.Drawing.OpenGL.Executors
|
||||
break;
|
||||
}
|
||||
|
||||
BlurgResult? result = Engine.Blurg.BuildString(font.Font, font.Size, color, args.Text);
|
||||
//BlurgResult? result = Engine.Blurg.BuildString(font.Font, font.Size, color, args.Text);
|
||||
|
||||
if (result == null)
|
||||
return;
|
||||
|
||||
Vector3 position = new Vector3(args.Position.X, args.Position.Y, args.Position.Z);
|
||||
ExecuteBlurgResult(result, position);
|
||||
|
||||
result.Dispose();
|
||||
// if (result == null)
|
||||
// return;
|
||||
//
|
||||
// Vector3 position = new Vector3(args.Position.X, args.Position.Y, args.Position.Z);
|
||||
// ExecuteBlurgResult(result, position);
|
||||
//
|
||||
// result.Dispose();
|
||||
}
|
||||
|
||||
private void ExecuteBlurgResult(BlurgResult result, Vector3 position)
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
using Dashboard.Drawing.OpenGL.Text;
|
||||
// using Dashboard.Drawing.OpenGL.Text;
|
||||
using Dashboard.OpenGL;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
@ -21,7 +21,7 @@ namespace Dashboard.Drawing.OpenGL
|
||||
if (bindingsContext != null)
|
||||
GLLoader.LoadBindings(bindingsContext);
|
||||
|
||||
Typesetter.Backend = BlurgEngine.Global;
|
||||
// Typesetter.Backend = BlurgEngine.Global;
|
||||
}
|
||||
|
||||
public ContextExecutor GetExecutor(IGLContext glContext)
|
||||
|
||||
@ -1,193 +0,0 @@
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.Numerics;
|
||||
using BlurgText;
|
||||
using Dashboard.OpenGL;
|
||||
using OpenTK.Graphics.OpenGL;
|
||||
using OPENGL = OpenTK.Graphics.OpenGL;
|
||||
|
||||
namespace Dashboard.Drawing.OpenGL.Text
|
||||
{
|
||||
public class BlurgEngine : IResourceManager, IGLDisposable, ITypeSetter
|
||||
{
|
||||
public string Name { get; } = "BlurgEngine";
|
||||
public Blurg Blurg { get; }
|
||||
public bool SystemFontsEnabled { get; }
|
||||
|
||||
private readonly List<int> _textures = new List<int>();
|
||||
|
||||
public BlurgEngine() : this(false)
|
||||
{
|
||||
}
|
||||
|
||||
private BlurgEngine(bool global)
|
||||
{
|
||||
if (global)
|
||||
Blurg = new Blurg(AllocateTextureGlobal, UpdateTextureGlobal);
|
||||
else
|
||||
Blurg = new Blurg(AllocateTexture, UpdateTexture);
|
||||
|
||||
SystemFontsEnabled = Blurg.EnableSystemFonts();
|
||||
}
|
||||
|
||||
~BlurgEngine()
|
||||
{
|
||||
Dispose(false, true);
|
||||
}
|
||||
|
||||
public SizeF MeasureString(IFont font, string value)
|
||||
{
|
||||
return MeasureStringInternal(InternFont(font), value);
|
||||
}
|
||||
|
||||
private SizeF MeasureStringInternal(DbBlurgFont font, string value)
|
||||
{
|
||||
Vector2 v = Blurg.MeasureString(font.Font, font.Size, value);
|
||||
return new SizeF(v.X, v.Y);
|
||||
}
|
||||
|
||||
public IFont LoadFont(Stream stream)
|
||||
{
|
||||
string path;
|
||||
Stream dest;
|
||||
for (int i = 0;; i++)
|
||||
{
|
||||
path = Path.GetTempFileName();
|
||||
try
|
||||
{
|
||||
dest = File.Open(path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
if (i < 3)
|
||||
continue;
|
||||
else
|
||||
throw new Exception("Could not open a temporary file for writing the font.", ex);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
stream.CopyTo(dest);
|
||||
dest.Dispose();
|
||||
|
||||
DbBlurgFont font = (DbBlurgFont)LoadFont(path);
|
||||
return font;
|
||||
}
|
||||
|
||||
public IFont LoadFont(string path)
|
||||
{
|
||||
BlurgFont? font = Blurg.AddFontFile(path) ?? throw new Exception("Failed to load the font file.");
|
||||
return new DbBlurgFont(Blurg, font, 12f) { Path = path };
|
||||
}
|
||||
|
||||
public IFont LoadFont(NamedFont font)
|
||||
{
|
||||
// Ignore the stretch argument.
|
||||
bool italic = font.Slant != FontSlant.Normal;
|
||||
BlurgFont? loaded = Blurg.QueryFont(font.Family, new BlurgText.FontWeight((int)font.Weight), italic);
|
||||
|
||||
if (loaded != null)
|
||||
return new DbBlurgFont(Blurg, loaded, 12f);
|
||||
else
|
||||
throw new Exception("Font not found.");
|
||||
}
|
||||
|
||||
public DbBlurgFont InternFont(IFont font)
|
||||
{
|
||||
if (font is NamedFont named)
|
||||
{
|
||||
return (DbBlurgFont)LoadFont(named);
|
||||
}
|
||||
else if (font is DbBlurgFont dblurg)
|
||||
{
|
||||
if (dblurg.Owner != Blurg)
|
||||
{
|
||||
if (dblurg.Path == null)
|
||||
return (DbBlurgFont)LoadFont(new NamedFont(dblurg.Family, dblurg.Size, dblurg.Weight,
|
||||
dblurg.Slant,
|
||||
dblurg.Stretch));
|
||||
else
|
||||
return (DbBlurgFont)LoadFont(dblurg.Path);
|
||||
}
|
||||
else
|
||||
{
|
||||
return dblurg;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Unsupported font resource.");
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateTexture(IntPtr texture, IntPtr buffer, int x, int y, int width, int height)
|
||||
{
|
||||
GL.BindTexture(TextureTarget.Texture2d, (int)texture);
|
||||
GL.TexSubImage2D(TextureTarget.Texture2d, 0, x, y, width, height, OPENGL.PixelFormat.Rgba, PixelType.UnsignedByte, buffer);
|
||||
// GL.TexSubImage2D(TextureTarget.Texture2d, 0, x, y, width, height, OPENGL.PixelFormat.Red, PixelType.Byte, buffer);
|
||||
}
|
||||
|
||||
private IntPtr AllocateTexture(int width, int height)
|
||||
{
|
||||
int texture = GL.GenTexture();
|
||||
|
||||
GL.BindTexture(TextureTarget.Texture2d, texture);
|
||||
GL.TexImage2D(TextureTarget.Texture2d, 0, InternalFormat.Rgba, width, height, 0, OPENGL.PixelFormat.Rgba, PixelType.UnsignedByte, IntPtr.Zero);
|
||||
// GL.TexImage2D(TextureTarget.Texture2d, 0, InternalFormat.R8, width, height, 0, OPENGL.PixelFormat.Red, PixelType.Byte, IntPtr.Zero);
|
||||
|
||||
GL.TexParameteri(TextureTarget.Texture2d, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
|
||||
GL.TexParameteri(TextureTarget.Texture2d, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
|
||||
// GL.TexParameteri(TextureTarget.Texture2d, TextureParameterName.TextureSwizzleR, (int)TextureSwizzle.One);
|
||||
// GL.TexParameteri(TextureTarget.Texture2d, TextureParameterName.TextureSwizzleG, (int)TextureSwizzle.One);
|
||||
// GL.TexParameteri(TextureTarget.Texture2d, TextureParameterName.TextureSwizzleB, (int)TextureSwizzle.One);
|
||||
// GL.TexParameteri(TextureTarget.Texture2d, TextureParameterName.TextureSwizzleA, (int)TextureSwizzle.Red);
|
||||
|
||||
_textures.Add(texture);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
private bool _isDisposed = false;
|
||||
|
||||
private void Dispose(bool disposing, bool safeExit)
|
||||
{
|
||||
if (_isDisposed)
|
||||
return;
|
||||
_isDisposed = true;
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
Blurg.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
if (safeExit)
|
||||
{
|
||||
foreach (int texture in _textures)
|
||||
ContextCollector.Global.DeleteTexture(texture);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose() => Dispose(true, true);
|
||||
|
||||
public void Dispose(bool safeExit) => Dispose(true, safeExit);
|
||||
|
||||
/// <summary>
|
||||
/// The global Blurg engine implements the needed methods for command queues to work.
|
||||
/// </summary>
|
||||
public static BlurgEngine Global { get; } = new BlurgEngine(true);
|
||||
|
||||
private static void UpdateTextureGlobal(IntPtr userdata, IntPtr buffer, int x, int y, int width, int height)
|
||||
{
|
||||
// Report the user error.
|
||||
Debug.WriteLine("Attempt to create or update a texture from the global BlurgEngine.", "Dashboard/BlurgEngine");
|
||||
}
|
||||
|
||||
private static IntPtr AllocateTextureGlobal(int width, int height)
|
||||
{
|
||||
Debug.WriteLine("Attempt to create or update a texture from the global BlurgEngine.", "Dashboard/BlurgEngine");
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,13 +0,0 @@
|
||||
namespace Dashboard.Drawing.OpenGL.Text
|
||||
{
|
||||
public class BlurgFontExtension : IDrawExtension
|
||||
{
|
||||
public string Name { get; } = "BLURG_Font";
|
||||
public IReadOnlyList<IDrawExtension> Requires { get; } = new [] { FontExtension.Instance };
|
||||
public IReadOnlyList<IDrawCommand> Commands { get; } = new IDrawCommand[] { };
|
||||
|
||||
private BlurgFontExtension() {}
|
||||
|
||||
public static readonly BlurgFontExtension Instance = new BlurgFontExtension();
|
||||
}
|
||||
}
|
||||
@ -1,30 +0,0 @@
|
||||
using BlurgText;
|
||||
|
||||
namespace Dashboard.Drawing.OpenGL.Text
|
||||
{
|
||||
public class DbBlurgFont : IFont
|
||||
{
|
||||
public IDrawExtension Kind { get; } = BlurgFontExtension.Instance;
|
||||
public Blurg Owner { get; }
|
||||
public BlurgFont Font { get; }
|
||||
public float Size { get; }
|
||||
public string Family => Font.FamilyName;
|
||||
public FontWeight Weight => (FontWeight)Font.Weight.Value;
|
||||
public FontSlant Slant => Font.Italic ? FontSlant.Italic : FontSlant.Normal;
|
||||
public FontStretch Stretch => FontStretch.Normal;
|
||||
|
||||
internal string? Path { get; init; }
|
||||
|
||||
public DbBlurgFont(Blurg owner, BlurgFont font, float size)
|
||||
{
|
||||
Owner = owner;
|
||||
Font = font;
|
||||
Size = size;
|
||||
}
|
||||
|
||||
public DbBlurgFont WithSize(float size)
|
||||
{
|
||||
return new DbBlurgFont(Owner, Font, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,52 +0,0 @@
|
||||
using System.Linq;
|
||||
|
||||
namespace Dashboard.Drawing
|
||||
{
|
||||
public class FontExtension : DrawExtension
|
||||
{
|
||||
private FontExtension() : base("DB_Font", Enumerable.Empty<DrawExtension>())
|
||||
{
|
||||
}
|
||||
|
||||
public static readonly IDrawExtension Instance = new FontExtension();
|
||||
}
|
||||
|
||||
public interface IFont : IDrawResource
|
||||
{
|
||||
public string Family { get; }
|
||||
public float Size { get; }
|
||||
public FontWeight Weight { get; }
|
||||
public FontSlant Slant { get; }
|
||||
public FontStretch Stretch { get; }
|
||||
}
|
||||
|
||||
public struct NamedFont : IFont
|
||||
{
|
||||
public IDrawExtension Kind { get; } = Instance;
|
||||
|
||||
public string Family { get; }
|
||||
public float Size { get; }
|
||||
public FontWeight Weight { get; }
|
||||
public FontSlant Slant { get; }
|
||||
public FontStretch Stretch { get; }
|
||||
|
||||
public NamedFont(string family, float size, FontWeight weight = FontWeight.Normal,
|
||||
FontSlant slant = FontSlant.Normal, FontStretch stretch = FontStretch.Normal)
|
||||
{
|
||||
Family = family;
|
||||
Size = size;
|
||||
Weight = weight;
|
||||
Slant = slant;
|
||||
Stretch = Stretch;
|
||||
}
|
||||
|
||||
private static readonly IDrawExtension Instance = new Extension();
|
||||
|
||||
private class Extension : DrawExtension
|
||||
{
|
||||
public Extension() : base("DB_Font_Named", [FontExtension.Instance])
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -9,7 +9,7 @@ namespace Dashboard.Drawing
|
||||
{
|
||||
public TextCommand TextCommand { get; }
|
||||
|
||||
private TextExtension() : base("DB_Text", new [] { FontExtension.Instance, BrushExtension.Instance })
|
||||
private TextExtension() : base("DB_Text", new [] { BrushExtension.Instance })
|
||||
{
|
||||
TextCommand = new TextCommand(this);
|
||||
}
|
||||
@ -40,7 +40,7 @@ namespace Dashboard.Drawing
|
||||
|
||||
header = new Header()
|
||||
{
|
||||
Font = queue.RequireResource(obj.Font),
|
||||
// Font = queue.RequireResource(obj.Font),
|
||||
TextBrush = queue.RequireResource(obj.TextBrush),
|
||||
BorderBrush = (obj.BorderBrush != null) ? queue.RequireResource(obj.BorderBrush) : -1,
|
||||
BorderRadius = (obj.BorderBrush != null) ? obj.BorderRadius : 0f,
|
||||
|
||||
@ -20,7 +20,7 @@ namespace Dashboard.Drawing
|
||||
|
||||
IFont LoadFont(Stream stream);
|
||||
IFont LoadFont(string path);
|
||||
IFont LoadFont(NamedFont font);
|
||||
// IFont LoadFont(NamedFont font);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -55,15 +55,16 @@ namespace Dashboard.Drawing
|
||||
return Backend.LoadFont(file.FullName);
|
||||
}
|
||||
|
||||
public static IFont LoadFont(NamedFont font)
|
||||
{
|
||||
return Backend.LoadFont(font);
|
||||
}
|
||||
// public static IFont LoadFont(NamedFont font)
|
||||
// {
|
||||
// return Backend.LoadFont(font);
|
||||
// }
|
||||
|
||||
public static IFont LoadFont(string family, float size, FontWeight weight = FontWeight.Normal,
|
||||
FontSlant slant = FontSlant.Normal, FontStretch stretch = FontStretch.Normal)
|
||||
{
|
||||
return LoadFont(new NamedFont(family, size, weight, slant, stretch));
|
||||
// return LoadFont(new NamedFont(family, size, weight, slant, stretch));
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
private class UndefinedTypeSetter : ITypeSetter
|
||||
@ -94,11 +95,11 @@ namespace Dashboard.Drawing
|
||||
return default;
|
||||
}
|
||||
|
||||
public IFont LoadFont(NamedFont font)
|
||||
{
|
||||
Except();
|
||||
return default;
|
||||
}
|
||||
// public IFont LoadFont(NamedFont font)
|
||||
// {
|
||||
// Except();
|
||||
// return default;
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
using System.Collections.Concurrent;
|
||||
using OpenTK.Graphics.OpenGL;
|
||||
|
||||
namespace Dashboard.Drawing.OpenGL
|
||||
namespace Dashboard.OpenGL
|
||||
{
|
||||
public class ContextCollector : IDisposable
|
||||
{
|
||||
97
Dashboard.OpenGL/Drawing/DeviceContextBase.cs
Normal file
97
Dashboard.OpenGL/Drawing/DeviceContextBase.cs
Normal file
@ -0,0 +1,97 @@
|
||||
using System.Drawing;
|
||||
using System.Numerics;
|
||||
using Dashboard.Drawing;
|
||||
using Dashboard.Pal;
|
||||
using OpenTK.Graphics.OpenGL;
|
||||
using OpenTK.Graphics.Wgl;
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace Dashboard.OpenGL.Drawing
|
||||
{
|
||||
public class DeviceContextBase : IDeviceContextBase
|
||||
{
|
||||
private readonly Stack<Matrix4x4> _transforms = new Stack<Matrix4x4>();
|
||||
private readonly Stack<RectangleF> _clipRegions = new Stack<RectangleF>();
|
||||
|
||||
public DeviceContext Context { get; private set; } = null!;
|
||||
IContextBase IContextExtensionBase.Context => Context;
|
||||
public string DriverName => "Dashboard OpenGL Device Context";
|
||||
public string DriverVendor => "Dashboard";
|
||||
public Version DriverVersion => new Version(0, 1);
|
||||
public RectangleF ClipRegion => _clipRegions.Peek();
|
||||
public Matrix4x4 Transforms => _transforms.Peek();
|
||||
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public void Require(DeviceContext context)
|
||||
{
|
||||
Context = context;
|
||||
|
||||
ResetClip();
|
||||
ResetTransforms();
|
||||
}
|
||||
|
||||
|
||||
void IContextExtensionBase.Require(IContextBase context) => Require((DeviceContext)context);
|
||||
|
||||
public void ResetClip()
|
||||
{
|
||||
_clipRegions.Clear();
|
||||
|
||||
SizeF size = ((GLDeviceContext)Context).GLContext.FramebufferSize;
|
||||
_clipRegions.Push(new RectangleF(0,0, size.Width, size.Height));
|
||||
|
||||
SetClip(ClipRegion);
|
||||
}
|
||||
|
||||
public void PushClip(RectangleF clipRegion)
|
||||
{
|
||||
clipRegion = new RectangleF(ClipRegion.X + clipRegion.X, ClipRegion.Y + clipRegion.Y,
|
||||
Math.Min(ClipRegion.Right - clipRegion.X, clipRegion.Width),
|
||||
Math.Min(ClipRegion.Bottom - clipRegion.Y, clipRegion.Height));
|
||||
_clipRegions.Push(clipRegion);
|
||||
|
||||
SetClip(clipRegion);
|
||||
}
|
||||
|
||||
public void PopClip()
|
||||
{
|
||||
_clipRegions.Pop();
|
||||
SetClip(ClipRegion);
|
||||
}
|
||||
|
||||
private void SetClip(RectangleF rect)
|
||||
{
|
||||
SizeF size = ((GLDeviceContext)Context).GLContext.FramebufferSize;
|
||||
GL.Viewport(
|
||||
(int)Math.Round(rect.X),
|
||||
(int)Math.Round(size.Height - rect.Y - rect.Height),
|
||||
(int)Math.Round(rect.Width),
|
||||
(int)Math.Round(rect.Height));
|
||||
}
|
||||
|
||||
public void ResetTransforms()
|
||||
{
|
||||
SizeF size = ((GLDeviceContext)Context).GLContext.FramebufferSize;
|
||||
Matrix4x4 m = Matrix4x4.CreateOrthographicOffCenterLeftHanded(0, size.Width, size.Height, 0, 1, -1);
|
||||
|
||||
_transforms.Clear();
|
||||
_transforms.Push(m);
|
||||
}
|
||||
|
||||
public void PushTransforms(in Matrix4x4 matrix)
|
||||
{
|
||||
Matrix4x4 result = matrix * Transforms;
|
||||
_transforms.Push(result);
|
||||
}
|
||||
|
||||
public void PopTransforms()
|
||||
{
|
||||
_transforms.Pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2,7 +2,6 @@ using System.Drawing;
|
||||
using System.Numerics;
|
||||
using System.Runtime.InteropServices;
|
||||
using Dashboard.Drawing;
|
||||
using Dashboard.Drawing.OpenGL.Pal;
|
||||
using Dashboard.Pal;
|
||||
using OpenTK.Graphics.OpenGL;
|
||||
|
||||
@ -112,7 +111,7 @@ namespace Dashboard.OpenGL.Drawing
|
||||
GL.EnableVertexAttribArray(_program_acolor);
|
||||
|
||||
Size size = ((GLDeviceContext)Context).GLContext.FramebufferSize;
|
||||
Matrix4x4 view = Matrix4x4.CreateOrthographicOffCenter(0, size.Width, 0, size.Height, 1, -1);
|
||||
Matrix4x4 view = Context.ExtensionRequire<IDeviceContextBase>().Transforms;
|
||||
|
||||
GL.UseProgram(_program);
|
||||
|
||||
@ -152,7 +151,7 @@ namespace Dashboard.OpenGL.Drawing
|
||||
GL.EnableVertexAttribArray(_program_acolor);
|
||||
|
||||
Size size = ((GLDeviceContext)Context).GLContext.FramebufferSize;
|
||||
Matrix4x4 view = Matrix4x4.CreateOrthographicOffCenter(0, size.Width, 0, size.Height, 1, -1);
|
||||
Matrix4x4 view = Context.ExtensionRequire<IDeviceContextBase>().Transforms;
|
||||
|
||||
GL.UseProgram(_program);
|
||||
|
||||
@ -191,7 +190,7 @@ namespace Dashboard.OpenGL.Drawing
|
||||
GL.VertexAttribPointer(_program_acolor, 4, VertexAttribPointerType.Float, false, ImmediateVertex.Size, ImmediateVertex.ColorOffset);
|
||||
GL.EnableVertexAttribArray(_program_acolor);
|
||||
Size size = ((GLDeviceContext)Context).GLContext.FramebufferSize;
|
||||
Matrix4x4 view = Matrix4x4.CreateOrthographicOffCenter(0, size.Width, 0, size.Height, 1, -1);
|
||||
Matrix4x4 view = Context.ExtensionRequire<IDeviceContextBase>().Transforms;
|
||||
|
||||
GL.UseProgram(_program);
|
||||
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Immutable;
|
||||
using Dashboard.OpenGL;
|
||||
using Dashboard.Drawing;
|
||||
using Dashboard.OpenGL.Drawing;
|
||||
using Dashboard.Pal;
|
||||
using Dashboard.Windowing;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using OpenTK.Graphics.OpenGL;
|
||||
|
||||
namespace Dashboard.Drawing.OpenGL.Pal
|
||||
namespace Dashboard.OpenGL
|
||||
{
|
||||
internal class GLContextBindingsContext(IGLContext context) : IBindingsContext
|
||||
{
|
||||
@ -21,6 +22,8 @@ namespace Dashboard.Drawing.OpenGL.Pal
|
||||
{
|
||||
public IGLContext GLContext { get; }
|
||||
|
||||
public ContextCollector Collector { get; } = new ContextCollector();
|
||||
|
||||
public override string DriverName => "Dashboard OpenGL Device Context";
|
||||
public override string DriverVendor => "Dashboard";
|
||||
public override Version DriverVersion => new Version(0, 1, 0);
|
||||
@ -36,7 +39,7 @@ namespace Dashboard.Drawing.OpenGL.Pal
|
||||
private readonly ConcurrentQueue<Task> _beforeDrawActions = new ConcurrentQueue<Task>();
|
||||
private readonly ConcurrentQueue<Task> _afterDrawActions = new ConcurrentQueue<Task>();
|
||||
|
||||
public GLDeviceContext(IGLContext context)
|
||||
public GLDeviceContext(Application app, IWindow? window, IGLContext context) : base(app, window)
|
||||
{
|
||||
GLContext = context;
|
||||
context.MakeCurrent();
|
||||
@ -62,6 +65,7 @@ namespace Dashboard.Drawing.OpenGL.Pal
|
||||
|
||||
Extensions = extensions.ToImmutableHashSet();
|
||||
|
||||
ExtensionPreload<DeviceContextBase>();
|
||||
ExtensionPreload<GLTextureExtension>();
|
||||
ExtensionPreload<ImmediateMode>();
|
||||
}
|
||||
@ -132,6 +136,9 @@ namespace Dashboard.Drawing.OpenGL.Pal
|
||||
base.Begin();
|
||||
|
||||
GLContext.MakeCurrent();
|
||||
IDeviceContextBase dc = ExtensionRequire<IDeviceContextBase>();
|
||||
dc.ResetClip();
|
||||
dc.ResetTransforms();
|
||||
|
||||
while (_beforeDrawActions.TryDequeue(out Task? action))
|
||||
{
|
||||
@ -1,9 +1,10 @@
|
||||
using System.Drawing;
|
||||
using Dashboard.Drawing;
|
||||
using Dashboard.Pal;
|
||||
using OpenTK.Graphics.OpenGL;
|
||||
using OGL = OpenTK.Graphics.OpenGL;
|
||||
|
||||
namespace Dashboard.Drawing.OpenGL.Pal
|
||||
namespace Dashboard.OpenGL
|
||||
{
|
||||
public class GLTextureExtension : ITextureExtension, IContextExtensionBase<GLDeviceContext>
|
||||
{
|
||||
@ -90,6 +91,11 @@ namespace Dashboard.Drawing.OpenGL.Pal
|
||||
private GLTextureExtension Extension { get; } = extension;
|
||||
private GLDeviceContext Context => Extension.Context;
|
||||
|
||||
~GLTexture()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
public void SetStorage(PixelFormat format, int width, int height, int depth, int levels)
|
||||
{
|
||||
if (!Context.IsRenderThread)
|
||||
@ -151,11 +157,19 @@ namespace Dashboard.Drawing.OpenGL.Pal
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public unsafe void Write<T>(PixelFormat format, ReadOnlySpan<T> buffer, int level = 0, int align = 4) where T : unmanaged
|
||||
void ITexture.Write<T>(PixelFormat format, ReadOnlySpan<T> buffer, int level, int align) => Write(format, buffer, level, align, null);
|
||||
|
||||
public unsafe void Write<T>(PixelFormat format, ReadOnlySpan<T> buffer, int level = 0, int align = 4, TimeSpan? timeout = null) where T : unmanaged
|
||||
{
|
||||
if (!Context.IsRenderThread)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
T[] bufferArray = buffer.ToArray();
|
||||
Task task = Context.InvokeBeforeDraw(() => Write<T>(format, bufferArray, level, align));
|
||||
|
||||
if (timeout.HasValue)
|
||||
task.Wait(timeout.Value);
|
||||
else
|
||||
task.Wait();
|
||||
}
|
||||
|
||||
Bind();
|
||||
@ -217,10 +231,36 @@ namespace Dashboard.Drawing.OpenGL.Pal
|
||||
GL.GenerateMipmap(Target);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
if (IsDisposed)
|
||||
return;
|
||||
IsDisposed = true;
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
if (Thread.CurrentThread != Context.RendererThread)
|
||||
{
|
||||
Context.Collector.DeleteTexture(Handle);
|
||||
}
|
||||
else
|
||||
{
|
||||
GL.DeleteTexture(Handle);
|
||||
}
|
||||
|
||||
Handle = 0;
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
Context.Collector.DeleteTexture(Handle);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsDisposed { get; private set; }
|
||||
|
||||
public void Dispose() => Dispose(false);
|
||||
|
||||
private void Bind()
|
||||
{
|
||||
@ -1,6 +1,6 @@
|
||||
using OpenTK.Graphics.OpenGL;
|
||||
|
||||
namespace Dashboard.Drawing.OpenGL
|
||||
namespace Dashboard.OpenGL
|
||||
{
|
||||
public static class ShaderUtil
|
||||
{
|
||||
@ -1,18 +0,0 @@
|
||||
using Dashboard.Events;
|
||||
using OpenTK.Platform;
|
||||
|
||||
namespace Dashboard.OpenTK.PAL2
|
||||
{
|
||||
public static class EventConverter
|
||||
{
|
||||
public static UiEventArgs? Convert(PlatformEventType type, EventArgs ea)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case PlatformEventType.KeyDown:
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
namespace Dashboard.OpenTK.PAL2
|
||||
{
|
||||
public static class OpenTKEventExtensions
|
||||
{
|
||||
// public static EventArgs ToDashboardEvent(this EventArgs)
|
||||
}
|
||||
}
|
||||
@ -1,9 +1,14 @@
|
||||
using Dashboard.Drawing.OpenGL.Pal;
|
||||
using System.Diagnostics;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Dashboard.Events;
|
||||
using Dashboard.OpenGL;
|
||||
using Dashboard.Pal;
|
||||
using Dashboard.Windowing;
|
||||
using OpenTK.Graphics;
|
||||
using OpenTK.Platform;
|
||||
using TK = OpenTK.Platform.Toolkit;
|
||||
using OPENTK = OpenTK.Platform;
|
||||
using DB = Dashboard.Events;
|
||||
|
||||
namespace Dashboard.OpenTK.PAL2
|
||||
{
|
||||
@ -16,10 +21,17 @@ namespace Dashboard.OpenTK.PAL2
|
||||
|
||||
private readonly List<PhysicalWindow> _windows = new List<PhysicalWindow>();
|
||||
|
||||
private readonly ConditionalWeakTable<WindowHandle, WindowExtraInfo> _windowHandleWindowMap =
|
||||
new ConditionalWeakTable<WindowHandle, WindowExtraInfo>();
|
||||
|
||||
private long _tick = Stopwatch.GetTimestamp();
|
||||
|
||||
public override IPhysicalWindow CreatePhysicalWindow()
|
||||
{
|
||||
PhysicalWindow window = new PhysicalWindow(GraphicsApiHints);
|
||||
PhysicalWindow window = new PhysicalWindow(this, GraphicsApiHints);
|
||||
|
||||
_windows.Add(window);
|
||||
_windowHandleWindowMap.Add(window.WindowHandle, new WindowExtraInfo(window));
|
||||
|
||||
return window;
|
||||
}
|
||||
@ -29,20 +41,184 @@ namespace Dashboard.OpenTK.PAL2
|
||||
return CreatePhysicalWindow();
|
||||
}
|
||||
|
||||
protected override void InitializeInternal()
|
||||
{
|
||||
base.InitializeInternal();
|
||||
|
||||
EventQueue.EventRaised += OnEventRaised;
|
||||
}
|
||||
|
||||
public override void RunEvents(bool wait)
|
||||
{
|
||||
TK.Window.ProcessEvents(wait);
|
||||
|
||||
long tock = Stopwatch.GetTimestamp();
|
||||
long elapsed = _tick - tock;
|
||||
float delta = (float)elapsed / Stopwatch.Frequency;
|
||||
TickEventArgs tickEvent = new TickEventArgs(delta);
|
||||
|
||||
_tick = tock;
|
||||
|
||||
for (int i = 0; i < _windows.Count; i++)
|
||||
{
|
||||
if (_windows[i].IsDisposed)
|
||||
PhysicalWindow window = _windows[i];
|
||||
|
||||
if (window.IsDisposed)
|
||||
{
|
||||
_windows.RemoveAt(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
_windows[i].Paint();
|
||||
window.SendEvent(this, tickEvent);
|
||||
window.SendEvent(this, new PaintEventArgs(window.DeviceContext));
|
||||
// For now we swap each window individually.
|
||||
((GLDeviceContext)window.DeviceContext).GLContext.SwapGroup.Swap();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEventRaised(PalHandle? handle, PlatformEventType type, EventArgs args)
|
||||
{
|
||||
if (handle is WindowHandle window)
|
||||
{
|
||||
OnWindowEventRaised(window, type, args);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
System.Diagnostics.Debugger.Break();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnWindowEventRaised(WindowHandle handle, PlatformEventType type, EventArgs args)
|
||||
{
|
||||
if (!_windowHandleWindowMap.TryGetValue(handle, out WindowExtraInfo? info))
|
||||
{
|
||||
Debugger?.LogDebug($"Unknown window handle {handle} received from OpenTK.");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case PlatformEventType.MouseDown:
|
||||
{
|
||||
MouseButtonDownEventArgs down = (MouseButtonDownEventArgs)args;
|
||||
MouseButtons buttons = (MouseButtons)(1 << (int)down.Button);
|
||||
ModifierKeys modifierKeys = GetModifierKeys(down.Modifiers);
|
||||
// TODO: modifier keys
|
||||
MouseButtonEventArgs down2 = new MouseButtonEventArgs(info.MousePosition, buttons, modifierKeys, false);
|
||||
info.Window.SendEvent(this, down2);
|
||||
break;
|
||||
}
|
||||
case PlatformEventType.MouseUp:
|
||||
{
|
||||
MouseButtonUpEventArgs up = (MouseButtonUpEventArgs)args;
|
||||
MouseButtons buttons = (MouseButtons)(1 << (int)up.Button);
|
||||
ModifierKeys modifierKeys = GetModifierKeys(up.Modifiers);
|
||||
// TODO: modifier keys
|
||||
MouseButtonEventArgs up2 = new MouseButtonEventArgs(info.MousePosition, buttons, modifierKeys, true);
|
||||
info.Window.SendEvent(this, up2);
|
||||
break;
|
||||
}
|
||||
case PlatformEventType.MouseMove:
|
||||
{
|
||||
OPENTK.MouseMoveEventArgs move = (OPENTK.MouseMoveEventArgs)args;
|
||||
Vector2 position = new Vector2(move.ClientPosition.X, move.ClientPosition.Y);
|
||||
DB.MouseMoveEventArgs move2 = new DB.MouseMoveEventArgs(position, position - info.MousePosition);
|
||||
|
||||
info.MousePosition = position;
|
||||
|
||||
info.Window.SendEvent(this, move2);
|
||||
break;
|
||||
}
|
||||
case PlatformEventType.Scroll:
|
||||
{
|
||||
ScrollEventArgs scroll = (ScrollEventArgs)args;
|
||||
Vector2 distance = new Vector2(scroll.Distance.X, scroll.Distance.Y);
|
||||
Vector2 delta = new Vector2(scroll.Delta.X, scroll.Delta.Y);
|
||||
MouseScrollEventArgs scroll2 = new MouseScrollEventArgs(distance, delta);
|
||||
info.Window.SendEvent(this, scroll2);
|
||||
break;
|
||||
}
|
||||
case PlatformEventType.KeyDown:
|
||||
{
|
||||
KeyDownEventArgs down = (KeyDownEventArgs)args;
|
||||
|
||||
ModifierKeys modifierKeys = GetModifierKeys(down.Modifiers);
|
||||
KeyCode keyCode = GetKeyCode(down.Key);
|
||||
ScanCode scanCode = GetScanCode(down.Scancode);
|
||||
|
||||
KeyboardButtonEventArgs up2 = new KeyboardButtonEventArgs(keyCode, scanCode, modifierKeys, false);
|
||||
info.Window.SendEvent(this, up2);
|
||||
break;
|
||||
}
|
||||
case PlatformEventType.KeyUp:
|
||||
{
|
||||
KeyUpEventArgs up = (KeyUpEventArgs)args;
|
||||
|
||||
ModifierKeys modifierKeys = GetModifierKeys(up.Modifiers);
|
||||
KeyCode keyCode = GetKeyCode(up.Key);
|
||||
ScanCode scanCode = GetScanCode(up.Scancode);
|
||||
|
||||
KeyboardButtonEventArgs up2 = new KeyboardButtonEventArgs(keyCode, scanCode, modifierKeys, true);
|
||||
info.Window.SendEvent(this, up2);
|
||||
break;
|
||||
}
|
||||
case PlatformEventType.Close:
|
||||
{
|
||||
info.Window.SendEvent(this, new WindowCloseEvent());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
Debugger?.LogDebug($"Unknown event type {type} with \"{args}\".");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static ModifierKeys GetModifierKeys(KeyModifier modifier)
|
||||
{
|
||||
ModifierKeys keys = 0;
|
||||
|
||||
keys |= modifier.HasFlag(KeyModifier.NumLock) ? ModifierKeys.NumLock : 0;
|
||||
keys |= modifier.HasFlag(KeyModifier.CapsLock) ? ModifierKeys.CapsLock : 0;
|
||||
keys |= modifier.HasFlag(KeyModifier.ScrollLock) ? ModifierKeys.ScrollLock : 0;
|
||||
|
||||
keys |= modifier.HasFlag(KeyModifier.LeftShift) ? ModifierKeys.LeftShift : 0;
|
||||
keys |= modifier.HasFlag(KeyModifier.LeftControl) ? ModifierKeys.LeftControl : 0;
|
||||
keys |= modifier.HasFlag(KeyModifier.LeftAlt) ? ModifierKeys.LeftAlt : 0;
|
||||
keys |= modifier.HasFlag(KeyModifier.LeftGUI) ? ModifierKeys.LeftMeta : 0;
|
||||
|
||||
keys |= modifier.HasFlag(KeyModifier.RightShift) ? ModifierKeys.RightShift : 0;
|
||||
keys |= modifier.HasFlag(KeyModifier.RightControl) ? ModifierKeys.RightControl : 0;
|
||||
keys |= modifier.HasFlag(KeyModifier.RightAlt) ? ModifierKeys.RightAlt : 0;
|
||||
keys |= modifier.HasFlag(KeyModifier.RightGUI) ? ModifierKeys.RightMeta : 0;
|
||||
|
||||
keys |= modifier.HasFlag(KeyModifier.Shift) ? ModifierKeys.Shift : 0;
|
||||
keys |= modifier.HasFlag(KeyModifier.Control) ? ModifierKeys.Control : 0;
|
||||
keys |= modifier.HasFlag(KeyModifier.Alt) ? ModifierKeys.Alt : 0;
|
||||
keys |= modifier.HasFlag(KeyModifier.GUI) ? ModifierKeys.Meta : 0;
|
||||
|
||||
// C# makes this cast as annoying as possible.
|
||||
keys |= (ModifierKeys)((((int)keys >> (int)ModifierKeys.RightBitPos) & 0xF) |
|
||||
(((int)keys >> (int)ModifierKeys.LeftBitPos) & 0xF));
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
private record WindowExtraInfo(PhysicalWindow Window)
|
||||
{
|
||||
public Vector2 MousePosition { get; set; } = Vector2.Zero;
|
||||
}
|
||||
|
||||
// TODO: Keycode and scancode tables.
|
||||
|
||||
private static KeyCode GetKeyCode(Key key) => key switch
|
||||
{
|
||||
_ => (KeyCode)0,
|
||||
};
|
||||
|
||||
private static ScanCode GetScanCode(Scancode scanCode) => scanCode switch
|
||||
{
|
||||
_ => (ScanCode)0,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Drawing;
|
||||
using System.Net;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Dashboard.Drawing;
|
||||
using Dashboard.Drawing.OpenGL.Pal;
|
||||
using Dashboard.Events;
|
||||
using Dashboard.OpenGL;
|
||||
using Dashboard.Pal;
|
||||
using Dashboard.Windowing;
|
||||
using OpenTK.Mathematics;
|
||||
@ -12,12 +14,15 @@ using TK = OpenTK.Platform.Toolkit;
|
||||
|
||||
namespace Dashboard.OpenTK.PAL2
|
||||
{
|
||||
public class PhysicalWindow : IPhysicalWindow, IDrawQueuePaintable, IEventListener, IDpiAwareWindow
|
||||
public class PhysicalWindow : IPhysicalWindow, IEventListener, IDpiAwareWindow
|
||||
{
|
||||
public DrawQueue DrawQueue { get; } = new DrawQueue();
|
||||
private readonly List<IEventListener> _listeners = new List<IEventListener>();
|
||||
|
||||
public Application Application { get; }
|
||||
public WindowHandle WindowHandle { get; }
|
||||
public DeviceContext DeviceContext { get; }
|
||||
public bool DoubleBuffered => true; // Always true for OpenTK windows.
|
||||
public IForm? Form { get; set; } = null;
|
||||
|
||||
public IWindowManager? WindowManager { get; set; }
|
||||
|
||||
@ -47,41 +52,40 @@ namespace Dashboard.OpenTK.PAL2
|
||||
set => TK.Window.SetClientSize(WindowHandle, new Vector2i((int)value.Width, (int)value.Height));
|
||||
}
|
||||
|
||||
public event EventHandler? Painting;
|
||||
public event EventHandler<AnimationTickEventArgs>? AnimationTimerEvent;
|
||||
public event EventHandler<MouseMoveEventArgs>? MouseMoved;
|
||||
public event EventHandler<MouseButtonEventArgs>? MouseButtonDown;
|
||||
public event EventHandler<MouseButtonEventArgs>? MouseButtonUp;
|
||||
public event EventHandler<MouseScrollEventArgs>? MouseScroll;
|
||||
public event EventHandler? EventRaised;
|
||||
|
||||
public PhysicalWindow(WindowHandle window)
|
||||
public PhysicalWindow(Application app, WindowHandle window)
|
||||
{
|
||||
Application = app;
|
||||
WindowHandle = window;
|
||||
DeviceContext = CreateDeviceContext(window, new OpenGLGraphicsApiHints());
|
||||
DeviceContext = CreateDeviceContext(app, this, new OpenGLGraphicsApiHints());
|
||||
AddWindow(this);
|
||||
}
|
||||
|
||||
public PhysicalWindow(WindowHandle window, OpenGLContextHandle context)
|
||||
public PhysicalWindow(Application app, WindowHandle window, OpenGLContextHandle context)
|
||||
{
|
||||
Application = app;
|
||||
WindowHandle = window;
|
||||
DeviceContext = new GLDeviceContext(new Pal2GLContext(window, context));
|
||||
DeviceContext = new GLDeviceContext(app, this, new Pal2GLContext(window, context));
|
||||
AddWindow(this);
|
||||
}
|
||||
|
||||
public PhysicalWindow(GraphicsApiHints hints)
|
||||
public PhysicalWindow(Application app, GraphicsApiHints hints)
|
||||
{
|
||||
Application = app;
|
||||
WindowHandle = TK.Window.Create(hints);
|
||||
DeviceContext = CreateDeviceContext(WindowHandle, hints);
|
||||
DeviceContext = CreateDeviceContext(app, this, hints);
|
||||
AddWindow(this);
|
||||
}
|
||||
|
||||
private static DeviceContext CreateDeviceContext(WindowHandle window, GraphicsApiHints hints)
|
||||
private static DeviceContext CreateDeviceContext(Application app, PhysicalWindow window, GraphicsApiHints hints)
|
||||
{
|
||||
WindowHandle handle = window.WindowHandle;
|
||||
switch (hints.Api)
|
||||
{
|
||||
case GraphicsApi.OpenGL:
|
||||
case GraphicsApi.OpenGLES:
|
||||
return new GLDeviceContext(new Pal2GLContext(window, TK.OpenGL.CreateFromWindow(window)));
|
||||
return new GLDeviceContext(app, window, new Pal2GLContext(handle, TK.OpenGL.CreateFromWindow(handle)));
|
||||
default:
|
||||
throw new Exception($"Unknown graphics API {hints.Api}.");
|
||||
}
|
||||
@ -99,31 +103,50 @@ namespace Dashboard.OpenTK.PAL2
|
||||
TK.Window.Destroy(WindowHandle);
|
||||
}
|
||||
|
||||
protected virtual void OnPaint()
|
||||
public virtual void SendEvent(object? sender, EventArgs args)
|
||||
{
|
||||
WindowManager?.Paint();
|
||||
Painting?.Invoke(this, EventArgs.Empty);
|
||||
switch (args)
|
||||
{
|
||||
case UiEventArgs ui:
|
||||
switch (ui.Type)
|
||||
{
|
||||
case UiEventType.ControlInvalidateVisual:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
public void Paint()
|
||||
args = TransformEvent(sender, args);
|
||||
|
||||
EventRaised?.Invoke(this, args);
|
||||
|
||||
lock (_listeners)
|
||||
{
|
||||
DrawQueue.Clear();
|
||||
OnPaint();
|
||||
foreach (IEventListener listener in _listeners)
|
||||
listener.SendEvent(this, args);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnAnimationTimerEvent(AnimationTickEventArgs ea) =>
|
||||
AnimationTimerEvent?.Invoke(this, ea);
|
||||
|
||||
protected virtual void OnMouseMoved(MouseMoveEventArgs ea) => MouseMoved?.Invoke(this, ea);
|
||||
|
||||
protected virtual void OnMouseButtonDown(MouseButtonEventArgs ea) => MouseButtonDown?.Invoke(this, ea);
|
||||
|
||||
protected virtual void OnMouseButtonUp(MouseButtonEventArgs ea) => MouseButtonUp?.Invoke(this, ea);
|
||||
|
||||
protected virtual void OnMouseScroll(MouseScrollEventArgs ea) => MouseScroll?.Invoke(this, ea);
|
||||
|
||||
public void SendEvent(EventArgs args)
|
||||
private EventArgs TransformEvent(object? sender, EventArgs args)
|
||||
{
|
||||
// TODO: future
|
||||
return args;
|
||||
}
|
||||
|
||||
public void SubcribeEvent(IEventListener listener)
|
||||
{
|
||||
lock (_listeners)
|
||||
{
|
||||
_listeners.Add(listener);
|
||||
}
|
||||
}
|
||||
|
||||
public void UnsubscribeEvent(IEventListener listener)
|
||||
{
|
||||
lock (_listeners)
|
||||
{
|
||||
_listeners.Remove(listener);
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly ConcurrentDictionary<WindowHandle, PhysicalWindow> _windows =
|
||||
|
||||
@ -29,6 +29,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dashboard.OpenGL", "Dashboa
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dashboard.StbImage", "Dashboard.StbImage\Dashboard.StbImage.csproj", "{85BCEB9E-DEC2-4A53-B2DA-6BFC6F3EE4E7}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dashboard.BlurgText.OpenGL", "Dashboard.BlurgText.OpenGL\Dashboard.BlurgText.OpenGL.csproj", "{14616F42-663B-4673-8561-5637FAD1B22F}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dashboard.BlurgText", "Dashboard.BlurgText\Dashboard.BlurgText.csproj", "{8C68EFB6-B477-48EC-9AAA-31E89883482B}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@ -72,6 +76,13 @@ Global
|
||||
{85BCEB9E-DEC2-4A53-B2DA-6BFC6F3EE4E7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{85BCEB9E-DEC2-4A53-B2DA-6BFC6F3EE4E7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{14616F42-663B-4673-8561-5637FAD1B22F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{14616F42-663B-4673-8561-5637FAD1B22F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{14616F42-663B-4673-8561-5637FAD1B22F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{14616F42-663B-4673-8561-5637FAD1B22F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{8C68EFB6-B477-48EC-9AAA-31E89883482B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8C68EFB6-B477-48EC-9AAA-31E89883482B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8C68EFB6-B477-48EC-9AAA-31E89883482B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8C68EFB6-B477-48EC-9AAA-31E89883482B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@ -80,5 +91,7 @@ Global
|
||||
{7C90B90B-DF31-439B-9080-CD805383B014} = {9D6CCC74-4DF3-47CB-B9B2-6BB75DF2BC40}
|
||||
{7B064228-2629-486E-95C6-BDDD4B4602C4} = {9B62A92D-ABF5-4704-B831-FD075515A82F}
|
||||
{85BCEB9E-DEC2-4A53-B2DA-6BFC6F3EE4E7} = {9B62A92D-ABF5-4704-B831-FD075515A82F}
|
||||
{14616F42-663B-4673-8561-5637FAD1B22F} = {9B62A92D-ABF5-4704-B831-FD075515A82F}
|
||||
{8C68EFB6-B477-48EC-9AAA-31E89883482B} = {9B62A92D-ABF5-4704-B831-FD075515A82F}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using Dashboard.Drawing;
|
||||
using Dashboard.Events;
|
||||
using Dashboard.Pal;
|
||||
using Dashboard.Windowing;
|
||||
|
||||
namespace Dashboard.Controls
|
||||
@ -25,7 +27,8 @@ namespace Dashboard.Controls
|
||||
public virtual Box2d ClientArea { get; set; }
|
||||
public bool IsFocused => _owner?.FocusedControl == this;
|
||||
|
||||
public event EventHandler? Painting;
|
||||
public event EventHandler<DeviceContext> Painting;
|
||||
public event EventHandler<TickEventArgs>? AnimationTick;
|
||||
public event EventHandler? OwnerChanged;
|
||||
public event EventHandler? ParentChanged;
|
||||
public event EventHandler? FocusGained;
|
||||
@ -38,14 +41,14 @@ namespace Dashboard.Controls
|
||||
Classes = new ClassSet(this);
|
||||
}
|
||||
|
||||
public virtual void OnPaint()
|
||||
public virtual void OnPaint(DeviceContext dc)
|
||||
{
|
||||
Painting?.Invoke(this, EventArgs.Empty);
|
||||
Painting?.Invoke(this, dc);
|
||||
}
|
||||
|
||||
public void Paint()
|
||||
public virtual void OnAnimationTick(TickEventArgs tick)
|
||||
{
|
||||
OnPaint();
|
||||
AnimationTick?.Invoke(this, tick);
|
||||
}
|
||||
|
||||
protected void InvokeDispose(bool disposing)
|
||||
@ -67,9 +70,28 @@ namespace Dashboard.Controls
|
||||
|
||||
public void Dispose() => InvokeDispose(true);
|
||||
|
||||
public void SendEvent(EventArgs args)
|
||||
public event EventHandler? EventRaised;
|
||||
|
||||
protected virtual void OnEventRaised(object? sender, EventArgs args)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
switch (args)
|
||||
{
|
||||
case PaintEventArgs paint:
|
||||
OnPaint(paint.DeviceContext);
|
||||
break;
|
||||
}
|
||||
|
||||
EventRaised?.Invoke(this, TransformEvent(sender, args));
|
||||
}
|
||||
|
||||
protected virtual EventArgs TransformEvent(object? sender, EventArgs args)
|
||||
{
|
||||
return args;
|
||||
}
|
||||
|
||||
public void SendEvent(object? sender, EventArgs args)
|
||||
{
|
||||
OnEventRaised(sender, args);
|
||||
}
|
||||
|
||||
internal static void SetParent(Control parent, Control child)
|
||||
|
||||
@ -1,15 +1,18 @@
|
||||
using System;
|
||||
using Dashboard.Drawing;
|
||||
using Dashboard.Events;
|
||||
using Dashboard.Pal;
|
||||
using Dashboard.Windowing;
|
||||
|
||||
namespace Dashboard.Controls
|
||||
{
|
||||
public class Form : Control
|
||||
public class Form : Control, IForm
|
||||
{
|
||||
public IWindow Window { get; }
|
||||
|
||||
public Image? WindowIcon { get; set; }
|
||||
public string? Title { get; set; } = "Untitled Form";
|
||||
public Control? Root { get; set; } = null;
|
||||
public Control? FocusedControl { get; private set; } = null;
|
||||
|
||||
public override Box2d ClientArea
|
||||
@ -18,12 +21,14 @@ namespace Dashboard.Controls
|
||||
set { }
|
||||
}
|
||||
|
||||
public event EventHandler<WindowCloseEvent>? Closing;
|
||||
|
||||
public Form(IWindow window)
|
||||
{
|
||||
Window = window;
|
||||
window.Form = this;
|
||||
}
|
||||
|
||||
|
||||
public void Focus(Control control)
|
||||
{
|
||||
if (FocusedControl != null)
|
||||
@ -32,5 +37,23 @@ namespace Dashboard.Controls
|
||||
FocusedControl = control;
|
||||
InvokeFocusGained(this, control);
|
||||
}
|
||||
|
||||
public override void OnPaint(DeviceContext dc)
|
||||
{
|
||||
dc.Begin();
|
||||
Root?.SendEvent(this, new PaintEventArgs(dc));
|
||||
dc.End();
|
||||
}
|
||||
|
||||
protected virtual void OnClosing(WindowCloseEvent ea)
|
||||
{
|
||||
Closing?.Invoke(this, ea);
|
||||
|
||||
if (ea.Cancel)
|
||||
return;
|
||||
|
||||
Dispose();
|
||||
Window.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ using System;
|
||||
using System.Drawing;
|
||||
using System.Numerics;
|
||||
using Dashboard.Drawing;
|
||||
using Dashboard.Pal;
|
||||
|
||||
namespace Dashboard.Controls
|
||||
{
|
||||
@ -26,11 +27,5 @@ namespace Dashboard.Controls
|
||||
SizeF sz = Typesetter.MeasureString(Font, Text);
|
||||
ClientArea = new Box2d(ClientArea.Min, ClientArea.Min + (Vector2)sz);
|
||||
}
|
||||
|
||||
public override void OnPaint()
|
||||
{
|
||||
base.OnPaint();
|
||||
// DrawQueue.Text(new Vector3(ClientArea.Min, 0), TextBrush, Text, Font);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,7 +4,8 @@ using System.Collections.Immutable;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using Dashboard.Resources;
|
||||
using Dashboard.Drawing;
|
||||
using Dashboard.Pal;
|
||||
using Dashboard.Windowing;
|
||||
|
||||
namespace Dashboard.Controls
|
||||
@ -77,11 +78,6 @@ namespace Dashboard.Controls
|
||||
s_errorIcon = Image.Load(str);
|
||||
}
|
||||
|
||||
public override void OnPaint()
|
||||
{
|
||||
base.OnPaint();
|
||||
}
|
||||
|
||||
public static MessageBox Create(IWindow window, string message, string title, MessageBoxIcon icon, MessageBoxButtons buttons)
|
||||
{
|
||||
return new MessageBox(window)
|
||||
|
||||
@ -1,23 +1,22 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Dashboard.Drawing;
|
||||
using Dashboard.Pal;
|
||||
using ReFuel.Stb;
|
||||
using ReFuel.Stb.Native;
|
||||
|
||||
namespace Dashboard.Resources
|
||||
namespace Dashboard.Drawing
|
||||
{
|
||||
public class Image(PixelFormat format, int width, int height, byte[] data, bool premultiplied = false) : IDisposable
|
||||
public class Image(ImageData data) : IDisposable
|
||||
{
|
||||
protected readonly ConditionalWeakTable<DeviceContext, ITexture> Textures =
|
||||
new ConditionalWeakTable<DeviceContext, ITexture>();
|
||||
|
||||
protected virtual TextureType TextureType => TextureType.Texture2D;
|
||||
public PixelFormat Format { get; } = format;
|
||||
public int Width { get; } = width;
|
||||
public int Height { get; } = height;
|
||||
public bool Premultiplied { get; } = premultiplied;
|
||||
public virtual TextureType Type => data.Type;
|
||||
public PixelFormat Format { get; } = data.Format;
|
||||
public int Width { get; } = data.Width;
|
||||
public int Height { get; } = data.Height;
|
||||
public int Depth { get; } = data.Depth;
|
||||
public int Levels { get; } = data.Levels;
|
||||
public bool Premultiplied { get; } = data.Premultiplied;
|
||||
|
||||
public bool IsDisposed { get; private set; } = false;
|
||||
|
||||
@ -32,9 +31,12 @@ namespace Dashboard.Resources
|
||||
return texture;
|
||||
|
||||
ITextureExtension ext = dc.ExtensionRequire<ITextureExtension>();
|
||||
texture = ext.CreateTexture(TextureType.Texture2D);
|
||||
texture.SetStorage(Format, Width, Height, 1, 0);
|
||||
texture.Write<byte>(Format, data);
|
||||
texture = ext.CreateTexture(Type);
|
||||
texture.SetStorage(Format, Width, Height, Depth, Levels);
|
||||
for (int i = 0; i < Levels; i++)
|
||||
{
|
||||
texture.Write<byte>(Format, data.Bitmap.AsSpan()[(int)data.GetLevelOffset(i)..], level: i, align: data.Alignment);
|
||||
}
|
||||
texture.Premultiplied = Premultiplied;
|
||||
texture.GenerateMipmaps();
|
||||
Textures.Add(dc, texture);
|
||||
@ -63,19 +65,10 @@ namespace Dashboard.Resources
|
||||
|
||||
public static Image Load(Stream stream)
|
||||
{
|
||||
using StbImage image = StbImage.Load(stream, StbiImageFormat.Rgba);
|
||||
ReadOnlySpan<byte> data = image.AsSpan<byte>();
|
||||
return new Image(
|
||||
image.Format switch
|
||||
{
|
||||
StbiImageFormat.Grey => PixelFormat.R8I,
|
||||
StbiImageFormat.Rgb => PixelFormat.Rgb8I,
|
||||
StbiImageFormat.Rgba => PixelFormat.Rgba8I,
|
||||
// StbiImageFormat.GreyAlpha) => PixelFormat.Rg8I,
|
||||
_ => throw new Exception("Unrecognized pixel format"),
|
||||
},
|
||||
image.Width, image.Height,
|
||||
data.ToArray());
|
||||
IImageLoader imageLoader = Application.Current.ExtensionRequire<IImageLoader>();
|
||||
return new Image(imageLoader.LoadImageData(stream));
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -8,9 +8,12 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Dashboard.BlurgText.OpenGL\Dashboard.BlurgText.OpenGL.csproj" />
|
||||
<ProjectReference Include="..\..\Dashboard.BlurgText\Dashboard.BlurgText.csproj" />
|
||||
<ProjectReference Include="..\..\Dashboard.Drawing.OpenGL\Dashboard.Drawing.OpenGL.csproj" />
|
||||
<ProjectReference Include="..\..\Dashboard.Drawing\Dashboard.Drawing.csproj" />
|
||||
<ProjectReference Include="..\..\Dashboard.ImmediateUI\Dashboard.ImmediateUI.csproj" />
|
||||
<ProjectReference Include="..\..\Dashboard.OpenTK\Dashboard.OpenTK.csproj" />
|
||||
<ProjectReference Include="..\..\Dashboard.StbImage\Dashboard.StbImage.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@ -1,8 +1,12 @@
|
||||
using System.Drawing;
|
||||
using System.Text;
|
||||
using BlurgText;
|
||||
using Dashboard.BlurgText;
|
||||
using Dashboard.BlurgText.OpenGL;
|
||||
using Dashboard.Controls;
|
||||
using Dashboard.Drawing;
|
||||
using Dashboard.Drawing.OpenGL.Pal;
|
||||
using Dashboard.Events;
|
||||
using Dashboard.OpenGL;
|
||||
using Dashboard.OpenTK.PAL2;
|
||||
using Dashboard.Pal;
|
||||
using Dashboard.StbImage;
|
||||
@ -10,6 +14,7 @@ using OpenTK.Graphics.OpenGL;
|
||||
using OpenTK.Mathematics;
|
||||
using OpenTK.Platform;
|
||||
using Image = Dashboard.Drawing.Image;
|
||||
using MouseMoveEventArgs = OpenTK.Platform.MouseMoveEventArgs;
|
||||
using TK = OpenTK.Platform.Toolkit;
|
||||
using Vector4 = System.Numerics.Vector4;
|
||||
|
||||
@ -61,6 +66,7 @@ StringBuilder builder = new StringBuilder();
|
||||
|
||||
app.Initialize();
|
||||
app.ExtensionRequire<StbImageLoader>();
|
||||
app.ExtensionLoad(new BlurgTextExtension(new BlurgTextExtensionFactory()));
|
||||
|
||||
window = (PhysicalWindow)app.CreatePhysicalWindow();
|
||||
|
||||
@ -106,7 +112,13 @@ TK.Window.SetMode(window.WindowHandle, WindowMode.Normal);
|
||||
|
||||
foreach (string str in typeof(Image).Assembly.GetManifestResourceNames()) Console.WriteLine(str);
|
||||
|
||||
window.Painting += (sender, ea) => {
|
||||
BlurgFont font = context.ExtensionRequire<BlurgDcExtension>().Blurg
|
||||
.QueryFont("Recursive Mono", FontWeight.Regular, false) ?? throw new Exception("Font not found");
|
||||
|
||||
window.EventRaised += (sender, ea) => {
|
||||
if (ea is not PaintEventArgs)
|
||||
return;
|
||||
|
||||
TK.Window.GetSize(window.WindowHandle, out Vector2i size);
|
||||
// executor.BeginFrame();
|
||||
//
|
||||
@ -178,11 +190,25 @@ window.Painting += (sender, ea) => {
|
||||
// executor.Draw(window.DrawQueue, new RectangleF(0, 0, size.X, size.Y), 1.5f /*(window as IDpiAwareWindow)?.Scale ?? 1*/);
|
||||
// executor.EndFrame();
|
||||
|
||||
context.Begin();
|
||||
|
||||
IImmediateMode imm = context.ExtensionRequire<IImmediateMode>();
|
||||
imm.ClearColor(Color.Magenta);
|
||||
imm.Line(new System.Numerics.Vector2(10, 10), new System.Numerics.Vector2(50, 50), 3, 0, new Vector4(0.2f, 0.2f, 1.0f, 1.0f));
|
||||
|
||||
context.GLContext.SwapGroup.Swap();
|
||||
BlurgDcExtension blurg = context.ExtensionRequire<BlurgDcExtension>();
|
||||
blurg.DrawBlurgFormattedText(new BlurgFormattedText("Hello world!", font) { DefaultSize = 64f }, new System.Numerics.Vector3(24, 24, 1));
|
||||
|
||||
context.End();
|
||||
};
|
||||
|
||||
app.Run(true, source.Token);
|
||||
|
||||
|
||||
class BlurgTextExtensionFactory : IBlurgDcExtensionFactory
|
||||
{
|
||||
public BlurgDcExtension CreateExtension(BlurgTextExtension appExtension, DeviceContext dc)
|
||||
{
|
||||
return new BlurgGLExtension();
|
||||
}
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user