Give label a proper implementation.

This commit is contained in:
2025-11-19 20:12:52 +03:00
parent 9ca309bd52
commit 5cba1ab7db
21 changed files with 480 additions and 315 deletions

View File

@@ -41,35 +41,6 @@ namespace Dashboard.BlurgText.OpenGL
Dispose(false); 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() private void UseProgram()
{ {
if (_program != 0) if (_program != 0)
@@ -150,7 +121,7 @@ namespace Dashboard.BlurgText.OpenGL
if (result.Count == 0) if (result.Count == 0)
return; return;
Matrix4x4 view = Matrix4x4.CreateTranslation(position) * Context.ExtensionRequire<IDeviceContextBase>().Transforms; Matrix4x4 view = Context.ExtensionRequire<IDeviceContextBase>().Transforms;
List<DrawCall> drawCalls = new List<DrawCall>(); List<DrawCall> drawCalls = new List<DrawCall>();
List<Vertex> vertices = new List<Vertex>(); List<Vertex> vertices = new List<Vertex>();

View File

@@ -11,7 +11,7 @@ namespace Dashboard.BlurgText
public BlurgDcExtension CreateExtension(BlurgTextExtension appExtension, DeviceContext dc); public BlurgDcExtension CreateExtension(BlurgTextExtension appExtension, DeviceContext dc);
} }
public class BlurgTextExtension(IBlurgDcExtensionFactory dcExtensionFactory) : IApplicationExtension, IFontLoader public class BlurgTextExtension(IBlurgDcExtensionFactory dcExtensionFactory) : IFontLoader
{ {
private readonly Blurg _blurg = new Blurg(GlobalTextureAllocation, GlobalTextureUpdate); private readonly Blurg _blurg = new Blurg(GlobalTextureAllocation, GlobalTextureUpdate);
@@ -27,6 +27,7 @@ namespace Dashboard.BlurgText
{ {
Context = context; Context = context;
context.DeviceContextCreated += OnDeviceContextCreated; context.DeviceContextCreated += OnDeviceContextCreated;
_blurg.EnableSystemFonts();
} }
void IContextExtensionBase.Require(IContextBase context) => Require((Application)context); void IContextExtensionBase.Require(IContextBase context) => Require((Application)context);
@@ -130,7 +131,7 @@ namespace Dashboard.BlurgText
} }
} }
public abstract class BlurgDcExtension : IDeviceContextExtension public abstract class BlurgDcExtension : IDeviceContextExtension, ITextRenderer
{ {
public abstract Blurg Blurg { get; } public abstract Blurg Blurg { get; }
public abstract string DriverName { get; } public abstract string DriverName { get; }
@@ -151,6 +152,35 @@ namespace Dashboard.BlurgText
DrawBlurgResult(result, position); DrawBlurgResult(result, position);
} }
protected 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.");
}
}
public abstract void Dispose(); public abstract void Dispose();
IContextBase IContextExtensionBase.Context => Context; IContextBase IContextExtensionBase.Context => Context;
@@ -161,5 +191,20 @@ namespace Dashboard.BlurgText
} }
void IContextExtensionBase.Require(IContextBase context) => Require((DeviceContext)context); void IContextExtensionBase.Require(IContextBase context) => Require((DeviceContext)context);
public Box2d MeasureText(IFont font, float size, string text)
{
BlurgFontProxy proxy = InternFont(font);
Vector2 sz = Blurg.MeasureString(proxy.Font, size * Context.ExtensionRequire<IDeviceContextBase>().Scale, text);
return new Box2d(0, 0, sz.X, sz.Y);
}
public void DrawText(Vector2 position, Vector4 color, float size, IFont font, string text)
{
BlurgFontProxy proxy = InternFont(font);
BlurgResult? result = Blurg.BuildString(proxy.Font, size * Context.ExtensionRequire<IDeviceContextBase>().Scale,
new BlurgColor((byte)(color.X * 255), (byte)(color.Y * 255), (byte)(color.Z * 255), (byte)(color.W * 255)), text);
DrawBlurgResult(result, new Vector3(position, 0));
}
} }
} }

View File

@@ -82,7 +82,7 @@ namespace Dashboard.Collections
public void Clear() => _objects.Clear(); public void Clear() => _objects.Clear();
public IEnumerator<T> GetEnumerator() => _objects.Values.GetEnumerator(); public IEnumerator<T> GetEnumerator() => _objects.Values.Distinct().GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
} }

View File

@@ -7,14 +7,24 @@ namespace Dashboard.Drawing
public interface IDeviceContextBase : IDeviceContextExtension public interface IDeviceContextBase : IDeviceContextExtension
{ {
RectangleF ClipRegion { get; } RectangleF ClipRegion { get; }
RectangleF ScissorRegion { get; }
Matrix4x4 Transforms { get; } Matrix4x4 Transforms { get; }
float Scale { get; }
float ScaleOverride { get; set; }
void ResetClip(); void ResetClip();
void PushClip(RectangleF clipRegion); void PushClip(RectangleF clipRegion);
void PopClip(); void PopClip();
void ResetScissor();
void PushScissor(RectangleF scissorRegion);
void PopScissor();
void ResetTransforms(); void ResetTransforms();
void PushTransforms(in Matrix4x4 matrix); void PushTransforms(in Matrix4x4 matrix);
void PopTransforms(); void PopTransforms();
void ClearColor(Color color);
void ClearDepth();
} }
} }

View File

@@ -1,3 +1,5 @@
using Dashboard.Pal;
namespace Dashboard.Drawing namespace Dashboard.Drawing
{ {
public interface IFont : IDisposable public interface IFont : IDisposable
@@ -16,7 +18,7 @@ namespace Dashboard.Drawing
} }
} }
public interface IFontLoader public interface IFontLoader : IApplicationExtension
{ {
IFont Load(FontInfo info); IFont Load(FontInfo info);
IFont Load(string path); IFont Load(string path);

View File

@@ -6,10 +6,6 @@ namespace Dashboard.Drawing
{ {
public interface IImmediateMode : IDeviceContextExtension public interface IImmediateMode : IDeviceContextExtension
{ {
void ClearColor(Color color);
void Line(Vector2 a, Vector2 b, float width, float depth, Vector4 color); void Line(Vector2 a, Vector2 b, float width, float depth, Vector4 color);
void Rectangle(Box2d rectangle, float depth, Vector4 color); void Rectangle(Box2d rectangle, float depth, Vector4 color);
void Image(Box2d rectangle, Box2d uv, float depth, ITexture texture); void Image(Box2d rectangle, Box2d uv, float depth, ITexture texture);

View File

@@ -16,6 +16,9 @@ namespace Dashboard.Pal
public bool IsDisposed { get; private set; } = false; public bool IsDisposed { get; private set; } = false;
public IContextDebugger? Debugger { get; set; } public IContextDebugger? Debugger { get; set; }
protected CancellationToken? CancellationToken { get; private set; }
protected bool Quit { get; set; } = false;
private readonly TypeDictionary<IApplicationExtension> _extensions = private readonly TypeDictionary<IApplicationExtension> _extensions =
new TypeDictionary<IApplicationExtension>(true); new TypeDictionary<IApplicationExtension>(true);
private readonly TypeDictionary<IApplicationExtension, Func<IApplicationExtension>> _preloadedExtensions = private readonly TypeDictionary<IApplicationExtension, Func<IApplicationExtension>> _preloadedExtensions =
@@ -58,15 +61,18 @@ namespace Dashboard.Pal
Initialize(); Initialize();
} }
public void Run() => Run(true, CancellationToken.None); public void Run() => Run(true, System.Threading.CancellationToken.None);
public void Run(bool wait) => Run(wait, CancellationToken.None); public void Run(bool wait) => Run(wait, System.Threading.CancellationToken.None);
public void Run(bool waitForEvents, CancellationToken token) public void Run(bool waitForEvents, CancellationToken token)
{ {
CancellationToken = token;
CancellationToken.Value.Register(() => Quit = true);
InitializeInternal(); InitializeInternal();
while (!token.IsCancellationRequested) while (!Quit && !token.IsCancellationRequested)
{ {
RunEvents(waitForEvents); RunEvents(waitForEvents);
} }
@@ -167,6 +173,7 @@ namespace Dashboard.Pal
{ {
if (!isDisposing) return; if (!isDisposing) return;
Quit = true;
foreach (IApplicationExtension extension in _extensions) foreach (IApplicationExtension extension in _extensions)
{ {
extension.Dispose(); extension.Dispose();

View File

@@ -2,9 +2,11 @@ using System.Drawing;
using System.Numerics; using System.Numerics;
using Dashboard.Drawing; using Dashboard.Drawing;
using Dashboard.Pal; using Dashboard.Pal;
using Dashboard.Windowing;
using OpenTK.Graphics.OpenGL; using OpenTK.Graphics.OpenGL;
using OpenTK.Graphics.Wgl; using OpenTK.Graphics.Wgl;
using OpenTK.Mathematics; using OpenTK.Mathematics;
using ColorBuffer = OpenTK.Graphics.OpenGL.ColorBuffer;
namespace Dashboard.OpenGL.Drawing namespace Dashboard.OpenGL.Drawing
{ {
@@ -12,14 +14,19 @@ namespace Dashboard.OpenGL.Drawing
{ {
private readonly Stack<Matrix4x4> _transforms = new Stack<Matrix4x4>(); private readonly Stack<Matrix4x4> _transforms = new Stack<Matrix4x4>();
private readonly Stack<RectangleF> _clipRegions = new Stack<RectangleF>(); private readonly Stack<RectangleF> _clipRegions = new Stack<RectangleF>();
private readonly Stack<RectangleF> _scissorRegions = new Stack<RectangleF>();
public DeviceContext Context { get; private set; } = null!; public DeviceContext Context { get; private set; } = null!;
IContextBase IContextExtensionBase.Context => Context; IContextBase IContextExtensionBase.Context => Context;
public string DriverName => "Dashboard OpenGL Device Context"; public string DriverName => "Dashboard OpenGL Device Context";
public string DriverVendor => "Dashboard"; public string DriverVendor => "Dashboard";
public Version DriverVersion => new Version(0, 1); public Version DriverVersion => new Version(0, 1);
public RectangleF ClipRegion => _clipRegions.Peek(); public RectangleF ClipRegion => _clipRegions.Peek();
public RectangleF ScissorRegion => _scissorRegions.Peek();
public Matrix4x4 Transforms => _transforms.Peek(); public Matrix4x4 Transforms => _transforms.Peek();
public float Scale => ScaleOverride > 0 ? ScaleOverride : (Context.Window as IDpiAwareWindow)?.Scale ?? 1;
public float ScaleOverride { get; set; } = -1f;
public void Dispose() public void Dispose()
@@ -32,6 +39,7 @@ namespace Dashboard.OpenGL.Drawing
Context = context; Context = context;
ResetClip(); ResetClip();
ResetScissor();
ResetTransforms(); ResetTransforms();
} }
@@ -64,6 +72,35 @@ namespace Dashboard.OpenGL.Drawing
SetClip(ClipRegion); SetClip(ClipRegion);
} }
public void ResetScissor()
{
GL.Disable(EnableCap.ScissorTest);
_scissorRegions.Clear();
SizeF size = ((GLDeviceContext)Context).GLContext.FramebufferSize;
_scissorRegions.Push(new RectangleF(0,0, size.Width, size.Height));
}
public void PushScissor(RectangleF scissorRegion)
{
GL.Enable(EnableCap.ScissorTest);
// scissorRegion = new RectangleF(scissorRegion.X + scissorRegion.X, scissorRegion.Y + scissorRegion.Y,
// Math.Min(ScissorRegion.Right - scissorRegion.X, scissorRegion.Width),
// Math.Min(ScissorRegion.Bottom - scissorRegion.Y, scissorRegion.Height));
_scissorRegions.Push(scissorRegion);
SetScissor(scissorRegion);
}
public void PopScissor()
{
if (_scissorRegions.Count == 1)
GL.Disable(EnableCap.ScissorTest);
_scissorRegions.Pop();
SetScissor(ClipRegion);
}
private void SetClip(RectangleF rect) private void SetClip(RectangleF rect)
{ {
SizeF size = ((GLDeviceContext)Context).GLContext.FramebufferSize; SizeF size = ((GLDeviceContext)Context).GLContext.FramebufferSize;
@@ -74,6 +111,16 @@ namespace Dashboard.OpenGL.Drawing
(int)Math.Round(rect.Height)); (int)Math.Round(rect.Height));
} }
void SetScissor(RectangleF rect)
{
SizeF size = ((GLDeviceContext)Context).GLContext.FramebufferSize;
GL.Scissor(
(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() public void ResetTransforms()
{ {
SizeF size = ((GLDeviceContext)Context).GLContext.FramebufferSize; SizeF size = ((GLDeviceContext)Context).GLContext.FramebufferSize;
@@ -93,5 +140,16 @@ namespace Dashboard.OpenGL.Drawing
{ {
_transforms.Pop(); _transforms.Pop();
} }
public void ClearColor(Color color)
{
GL.ClearColor(color.R / 255f, color.G / 255f, color.B / 255f, color.A / 255f);
GL.Clear(ClearBufferMask.ColorBufferBit);
}
public void ClearDepth()
{
GL.Clear(ClearBufferMask.DepthBufferBit);
}
} }
} }

View File

@@ -22,7 +22,6 @@ namespace Dashboard.OpenGL
public void Dispose() public void Dispose()
{ {
throw new NotImplementedException();
} }

View File

@@ -44,12 +44,27 @@ namespace Dashboard.OpenTK.PAL2
protected override void InitializeInternal() protected override void InitializeInternal()
{ {
base.InitializeInternal(); base.InitializeInternal();
CancellationToken?.Register(() =>
{
TK.Window.PostUserEvent(new ApplicationQuitEventArgs());
});
EventQueue.EventRaised += OnEventRaised; EventQueue.EventRaised += OnEventRaised;
} }
internal void RemoveWindow(PhysicalWindow window)
{
_windows.Remove(window);
}
public override void RunEvents(bool wait) public override void RunEvents(bool wait)
{ {
if (_windows.Count == 0)
{
Dispose();
return;
}
TK.Window.ProcessEvents(wait); TK.Window.ProcessEvents(wait);
long tock = Stopwatch.GetTimestamp(); long tock = Stopwatch.GetTimestamp();
@@ -85,7 +100,7 @@ namespace Dashboard.OpenTK.PAL2
} }
else else
{ {
System.Diagnostics.Debugger.Break(); // System.Diagnostics.Debugger.Break();
} }
} }
@@ -99,6 +114,13 @@ namespace Dashboard.OpenTK.PAL2
switch (type) switch (type)
{ {
case PlatformEventType.UserMessage:
if (args is ApplicationQuitEventArgs)
{
Quit = true;
return;
}
break;
case PlatformEventType.MouseDown: case PlatformEventType.MouseDown:
{ {
MouseButtonDownEventArgs down = (MouseButtonDownEventArgs)args; MouseButtonDownEventArgs down = (MouseButtonDownEventArgs)args;
@@ -221,4 +243,8 @@ namespace Dashboard.OpenTK.PAL2
_ => (ScanCode)0, _ => (ScanCode)0,
}; };
} }
internal class ApplicationQuitEventArgs() : EventArgs
{
}
} }

View File

@@ -58,7 +58,6 @@ namespace Dashboard.OpenTK.PAL2
Application = app; Application = app;
WindowHandle = window; WindowHandle = window;
DeviceContext = CreateDeviceContext(app, this, new OpenGLGraphicsApiHints()); DeviceContext = CreateDeviceContext(app, this, new OpenGLGraphicsApiHints());
AddWindow(this);
} }
public PhysicalWindow(Application app, WindowHandle window, OpenGLContextHandle context) public PhysicalWindow(Application app, WindowHandle window, OpenGLContextHandle context)
@@ -66,7 +65,6 @@ namespace Dashboard.OpenTK.PAL2
Application = app; Application = app;
WindowHandle = window; WindowHandle = window;
DeviceContext = new GLDeviceContext(app, this, new Pal2GLContext(window, context)); DeviceContext = new GLDeviceContext(app, this, new Pal2GLContext(window, context));
AddWindow(this);
} }
public PhysicalWindow(Application app, GraphicsApiHints hints) public PhysicalWindow(Application app, GraphicsApiHints hints)
@@ -74,7 +72,6 @@ namespace Dashboard.OpenTK.PAL2
Application = app; Application = app;
WindowHandle = TK.Window.Create(hints); WindowHandle = TK.Window.Create(hints);
DeviceContext = CreateDeviceContext(app, this, hints); DeviceContext = CreateDeviceContext(app, this, hints);
AddWindow(this);
} }
private static DeviceContext CreateDeviceContext(Application app, PhysicalWindow window, GraphicsApiHints hints) private static DeviceContext CreateDeviceContext(Application app, PhysicalWindow window, GraphicsApiHints hints)
@@ -96,27 +93,18 @@ namespace Dashboard.OpenTK.PAL2
if (IsDisposed) return; if (IsDisposed) return;
IsDisposed = true; IsDisposed = true;
RemoveWindow(this);
(DeviceContext as IDisposable)?.Dispose(); (DeviceContext as IDisposable)?.Dispose();
((Pal2Application)Application).RemoveWindow(this);
TK.Window.Destroy(WindowHandle); TK.Window.Destroy(WindowHandle);
} }
public virtual void SendEvent(object? sender, EventArgs args) public virtual void SendEvent(object? sender, EventArgs args)
{ {
switch (args)
{
case UiEventArgs ui:
switch (ui.Type)
{
case UiEventType.ControlInvalidateVisual:
break;
}
break;
}
args = TransformEvent(sender, args); args = TransformEvent(sender, args);
Form?.SendEvent(this, args);
EventRaised?.Invoke(this, args); EventRaised?.Invoke(this, args);
lock (_listeners) lock (_listeners)
@@ -148,42 +136,6 @@ namespace Dashboard.OpenTK.PAL2
} }
} }
private static readonly ConcurrentDictionary<WindowHandle, PhysicalWindow> _windows =
new ConcurrentDictionary<WindowHandle, PhysicalWindow>();
static PhysicalWindow()
{
EventQueue.EventRaised += EventQueueOnEventRaised;
}
private static void AddWindow(PhysicalWindow window)
{
_windows.TryAdd(window.WindowHandle, window);
}
private static void RemoveWindow(PhysicalWindow window)
{
_windows.TryRemove(window.WindowHandle, out _);
}
private static void EventQueueOnEventRaised(PalHandle? handle, PlatformEventType type, EventArgs args)
{
if (handle is not WindowHandle windowHandle)
return;
if (!_windows.TryGetValue(windowHandle, out PhysicalWindow? window))
return;
switch (type)
{
case PlatformEventType.MouseMove:
case PlatformEventType.Scroll:
case PlatformEventType.MouseUp:
case PlatformEventType.MouseDown:
break;
}
}
public float Dpi => Scale * 96f; public float Dpi => Scale * 96f;
public float Scale public float Scale

View File

@@ -1,77 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace Dashboard.Controls
{
public enum ClassChangeType
{
Added,
Removed,
}
public record struct ClassChanged(object? Owner, string ClassName, ClassChangeType Type);
public class ClassSet : ICollection<string>
{
public int Count => _classes.Count;
public bool IsReadOnly => false;
public object? Owner { get; }
public event EventHandler<ClassChanged>? ClassChanged;
private readonly HashSet<string> _classes = new HashSet<string>();
public ClassSet(object? owner)
{
Owner = owner;
}
public IEnumerator<string> GetEnumerator() => _classes.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void Add(string item)
{
if (_classes.Add(item))
{
OnClassAdded(item);
}
}
public void Clear()
{
foreach (string @class in _classes)
OnClassRemoved(@class);
_classes.Clear();
}
public bool Contains(string item) => _classes.Contains(item);
public void CopyTo(string[] array, int arrayIndex) => _classes.CopyTo(array, arrayIndex);
public bool Remove(string item)
{
if (_classes.Remove(item))
{
OnClassRemoved(item);
return true;
}
return false;
}
private void OnClassAdded(string @class)
{
ClassChanged?.Invoke(this, new ClassChanged(Owner, @class, ClassChangeType.Added));
}
private void OnClassRemoved(string @class)
{
ClassChanged?.Invoke(this, new ClassChanged(Owner, @class, ClassChangeType.Removed));
}
}
}

View File

@@ -0,0 +1,137 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.Common;
using System.Drawing;
using System.Numerics;
using Dashboard.Drawing;
using Dashboard.Events;
using Dashboard.Pal;
namespace Dashboard.Controls
{
public class Container : Control, IList<Control>
{
private readonly List<Control> _controls = new List<Control>();
public int Count => _controls.Count;
public bool IsReadOnly => false;
public event EventHandler<ContainerChildAddedEventArgs>? ChildAdded;
public event EventHandler<ContainerChildRemovedEventArgs>? ChildRemoved;
public Control this[int index]
{
get => _controls[index];
set => _controls[index] = value;
}
protected virtual void LayoutChildren()
{
// TODO: not position everything using absolute coordinates.
foreach (Control child in _controls)
{
child.ClientArea = new Box2d(child.Position, child.Position + child.Size);
}
}
public override void OnPaint(DeviceContext dc)
{
base.OnPaint(dc);
var dcb = dc.ExtensionRequire<IDeviceContextBase>();
dcb.PushClip(new RectangleF(ClientArea.Left, ClientArea.Bottom, ClientArea.Size.X, ClientArea.Size.Y));
// dcb.PushTransforms(Matrix4x4.CreateTranslation(ClientArea.Left, ClientArea.Bottom, 0));
LayoutChildren();
foreach (Control child in _controls)
{
if (child.DisplayMode == DisplayMode.None)
continue;
child.SendEvent(this, new PaintEventArgs(dc));
}
dcb.PopClip();
}
public IEnumerator<Control> GetEnumerator()
{
return _controls.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable)_controls).GetEnumerator();
}
public void Add(Control item)
{
SetParent(this, item);
_controls.Add(item);
ChildAdded?.Invoke(this, new ContainerChildAddedEventArgs(this, item));
}
public void Clear()
{
foreach (Control control in this)
{
ChildRemoved?.Invoke(this, new ContainerChildRemovedEventArgs(this, control));
}
_controls.Clear();
}
public bool Contains(Control item)
{
return _controls.Contains(item);
}
public void CopyTo(Control[] array, int arrayIndex)
{
_controls.CopyTo(array, arrayIndex);
}
public bool Remove(Control item)
{
if (!_controls.Remove(item))
return false;
ChildRemoved?.Invoke(this, new ContainerChildRemovedEventArgs(this, item));
return true;
}
public int IndexOf(Control item)
{
return _controls.IndexOf(item);
}
public void Insert(int index, Control item)
{
SetParent(this, item);
_controls.Insert(index, item);
ChildAdded?.Invoke(this, new ContainerChildAddedEventArgs(this, item));
}
public void RemoveAt(int index)
{
Control child = _controls[index];
_controls.RemoveAt(index);
ChildRemoved?.Invoke(this, new ContainerChildRemovedEventArgs(this, child));
}
}
public class ContainerChildAddedEventArgs(Container parent, Control child) : EventArgs
{
public Container Parent { get; } = parent;
public Control Child { get; } = child;
}
public class ContainerChildRemovedEventArgs(Container parent, Control child) : EventArgs
{
public Container Parent { get; } = parent;
public Control Child { get; } = child;
}
}

View File

@@ -1,16 +1,40 @@
using System; using System;
using System.Drawing;
using System.Numerics;
using Dashboard.Events; using Dashboard.Events;
using Dashboard.Pal; using Dashboard.Pal;
using Dashboard.Windowing; using Dashboard.Windowing;
namespace Dashboard.Controls namespace Dashboard.Controls
{ {
public enum DisplayMode
{
None,
Inline,
Block,
Flex,
Grid,
}
public enum FlowDirection
{
Row,
Column,
RowReverse,
ColumnReverse,
}
public enum PositionMode
{
Absolute,
Relative,
}
public class Control : IEventListener, IDisposable public class Control : IEventListener, IDisposable
{ {
private Form? _owner = null; private Form? _owner = null;
public string? Id { get; set; } public string? Id { get; set; }
public ClassSet Classes { get; }
public Form Owner public Form Owner
{ {
get => _owner ?? throw NoOwnerException; get => _owner ?? throw NoOwnerException;
@@ -26,7 +50,20 @@ namespace Dashboard.Controls
public virtual Box2d ClientArea { get; set; } public virtual Box2d ClientArea { get; set; }
public bool IsFocused => _owner?.FocusedControl == this; public bool IsFocused => _owner?.FocusedControl == this;
public event EventHandler<DeviceContext> Painting; // Layout properties for this control.
public Vector2 Size { get; set; } = new Vector2(50, 30);
public Vector2 MinimumSize { get; set; } = new Vector2(-1, -1);
public Vector2 MaximumSize { get; set; } = new Vector2(-1, -1);
public int ZIndex { get; set; } = -1;
public Vector2 Position { get; set; }
public PositionMode PositionMode { get; set; } = PositionMode.Relative;
public DisplayMode DisplayMode { get; set; } = DisplayMode.Inline;
public FlowDirection FlowDirection { get; set; } = FlowDirection.Row;
public int Row { get; set; }
public int Column { get; set; }
public bool HideOverflow { get; set; } = false;
public event EventHandler<DeviceContext>? Painting;
public event EventHandler<TickEventArgs>? AnimationTick; public event EventHandler<TickEventArgs>? AnimationTick;
public event EventHandler? OwnerChanged; public event EventHandler? OwnerChanged;
public event EventHandler? ParentChanged; public event EventHandler? ParentChanged;
@@ -35,11 +72,6 @@ namespace Dashboard.Controls
public event EventHandler? Disposing; public event EventHandler? Disposing;
public event EventHandler? Resized; public event EventHandler? Resized;
public Control()
{
Classes = new ClassSet(this);
}
public virtual void OnPaint(DeviceContext dc) public virtual void OnPaint(DeviceContext dc)
{ {
Painting?.Invoke(this, dc); Painting?.Invoke(this, dc);
@@ -93,7 +125,7 @@ namespace Dashboard.Controls
OnEventRaised(sender, args); OnEventRaised(sender, args);
} }
internal static void SetParent(Control parent, Control child) internal static void SetParent(Container parent, Control child)
{ {
child.Parent = parent; child.Parent = parent;
child.ParentChanged?.Invoke(child, EventArgs.Empty); child.ParentChanged?.Invoke(child, EventArgs.Empty);

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Drawing;
using Dashboard.Drawing; using Dashboard.Drawing;
using Dashboard.Events; using Dashboard.Events;
using Dashboard.Pal; using Dashboard.Pal;
@@ -6,13 +7,23 @@ using Dashboard.Windowing;
namespace Dashboard.Controls namespace Dashboard.Controls
{ {
public class Form : Control, IForm public class Form : Container, IForm
{ {
private string? _title = "Untitled Form";
public IWindow Window { get; } public IWindow Window { get; }
public Image? WindowIcon { get; set; } public Image? WindowIcon { get; set; }
public string? Title { get; set; } = "Untitled Form";
public Control? Root { get; set; } = null; public string? Title
{
get => _title;
set
{
_title = value;
Window.Title = _title ?? "";
}
}
public Brush Background { get; set; } = new SolidColorBrush(Color.SlateGray);
public Control? FocusedControl { get; private set; } = null; public Control? FocusedControl { get; private set; } = null;
public override Box2d ClientArea public override Box2d ClientArea
@@ -27,6 +38,8 @@ namespace Dashboard.Controls
{ {
Window = window; Window = window;
window.Form = this; window.Form = this;
Window.Title = _title;
} }
public void Focus(Control control) public void Focus(Control control)
@@ -41,7 +54,17 @@ namespace Dashboard.Controls
public override void OnPaint(DeviceContext dc) public override void OnPaint(DeviceContext dc)
{ {
dc.Begin(); dc.Begin();
Root?.SendEvent(this, new PaintEventArgs(dc));
var dcb = dc.ExtensionRequire<IDeviceContextBase>();
dcb.ResetScissor();
dcb.ResetTransforms();
if (Background is SolidColorBrush solidColorBrush)
dcb.ClearColor(solidColorBrush.Color);
foreach (Control child in this)
child.SendEvent(this, new PaintEventArgs(dc));
dc.End(); dc.End();
} }
@@ -55,5 +78,17 @@ namespace Dashboard.Controls
Dispose(); Dispose();
Window.Dispose(); Window.Dispose();
} }
protected override void OnEventRaised(object? sender, EventArgs args)
{
base.OnEventRaised(sender, args);
switch (args)
{
case WindowCloseEvent close:
OnClosing(close);
break;
}
}
} }
} }

View File

@@ -14,18 +14,39 @@ namespace Dashboard.Controls
public event EventHandler? TextChanged; public event EventHandler? TextChanged;
// protected IBrush TextBrush => throw new NotImplementedException(); // protected IBrush TextBrush => throw new NotImplementedException();
protected IFont Font => throw new NotImplementedException(); public Font Font { get; set; } = Drawing.Font.Create(new FontInfo("Rec Mono Linear"));
public float TextSize { get; set; } = 12f;
public Brush TextBrush { get; set; } = new SolidColorBrush(Color.Black);
protected virtual void OnTextChanged(string oldValue, string newValue) protected void CalculateSize(DeviceContext dc)
{ {
if (AutoSize) Box2d box = dc.ExtensionRequire<ITextRenderer>().MeasureText(Font.Base, TextSize, Text);
CalculateSize(); Size = box.Size;
ClientArea = new Box2d(ClientArea.Min, ClientArea.Min + Size);
} }
protected void CalculateSize() public override void OnPaint(DeviceContext dc)
{ {
// SizeF sz = Typesetter.MeasureString(Font, Text); base.OnPaint(dc);
// ClientArea = new Box2d(ClientArea.Min, ClientArea.Min + (Vector2)sz);
if (AutoSize)
CalculateSize(dc);
var dcb = dc.ExtensionRequire<IDeviceContextBase>();
if (HideOverflow)
dcb.PushScissor(new RectangleF(ClientArea.Left, ClientArea.Top, ClientArea.Size.X, ClientArea.Size.Y));
dcb.PushTransforms(Matrix4x4.CreateTranslation(ClientArea.Left, ClientArea.Top, 0));
var text = dc.ExtensionRequire<ITextRenderer>();
Color color = (TextBrush as SolidColorBrush)?.Color ?? Color.Black;
Vector4 colorVector = new Vector4(color.R / 255f, color.G / 255f, color.B / 255f, color.A / 255f);
text.DrawText(Vector2.Zero, colorVector, TextSize, Font.Base, Text);
if (HideOverflow)
dcb.PopScissor();
dcb.PopTransforms();
} }
} }
} }

View File

@@ -5,7 +5,6 @@ using System.Collections.ObjectModel;
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
using Dashboard.Drawing; using Dashboard.Drawing;
using Dashboard.Pal;
using Dashboard.Windowing; using Dashboard.Windowing;
namespace Dashboard.Controls namespace Dashboard.Controls
@@ -42,14 +41,20 @@ namespace Dashboard.Controls
public MessageBoxIcon Icon { get; set; } public MessageBoxIcon Icon { get; set; }
public Image? CustomImage { get; set; } public Image? CustomImage { get; set; }
public string? Message { get; set; }
public string? Message
{
get => _label.Text;
set => _label.Text = value ?? String.Empty;
}
public MessageBoxButtons Buttons { get; set; } public MessageBoxButtons Buttons { get; set; }
public ObservableCollection<string> CustomButtons { get; } = new ObservableCollection<string>(); public ObservableCollection<string> CustomButtons { get; } = new ObservableCollection<string>();
public int Result { get; private set; } public int Result { get; private set; }
public MessageBox(IWindow window) : base(window) public MessageBox(IWindow window) : base(window)
{ {
SetParent(this, _label); Add(_label);
} }
public static readonly Image s_questionIcon; public static readonly Image s_questionIcon;

View File

@@ -0,0 +1,13 @@
using System.Drawing;
namespace Dashboard.Drawing
{
public abstract class Brush
{
}
public class SolidColorBrush(Color color) : Brush
{
public Color Color { get; } = color;
}
}

42
Dashboard/Drawing/Font.cs Normal file
View File

@@ -0,0 +1,42 @@
using System;
using System.IO;
using System.Net.Mime;
using Dashboard.Pal;
namespace Dashboard.Drawing
{
public class Font(IFont iFont) : IFont
{
public IFont Base => iFont;
public string Family => iFont.Family;
public FontWeight Weight => iFont.Weight;
public FontSlant Slant => iFont.Slant;
public FontStretch Stretch => iFont.Stretch;
public void Dispose()
{
iFont.Dispose();
}
public static Font Create(Stream stream)
{
IFont iFont = Application.Current.ExtensionRequire<IFontLoader>().Load(stream);
return new Font(iFont);
}
public static Font Create(FontInfo info)
{
IFont iFont = Application.Current.ExtensionRequire<IFontLoader>().Load(info);
return new Font(iFont);
}
public static Font Create(string path)
{
IFont iFont = Application.Current.ExtensionRequire<IFontLoader>().Load(path);
return new Font(iFont);
}
}
}

View File

@@ -67,8 +67,6 @@ namespace Dashboard.Drawing
{ {
IImageLoader imageLoader = Application.Current.ExtensionRequire<IImageLoader>(); IImageLoader imageLoader = Application.Current.ExtensionRequire<IImageLoader>();
return new Image(imageLoader.LoadImageData(stream)); return new Image(imageLoader.LoadImageData(stream));
} }
} }
} }

View File

@@ -1,6 +1,4 @@
using System.Drawing; using BlurgText;
using System.Text;
using BlurgText;
using Dashboard.BlurgText; using Dashboard.BlurgText;
using Dashboard.BlurgText.OpenGL; using Dashboard.BlurgText.OpenGL;
using Dashboard.Controls; using Dashboard.Controls;
@@ -13,10 +11,7 @@ using Dashboard.StbImage;
using OpenTK.Graphics.OpenGL; using OpenTK.Graphics.OpenGL;
using OpenTK.Mathematics; using OpenTK.Mathematics;
using OpenTK.Platform; using OpenTK.Platform;
using Image = Dashboard.Drawing.Image;
using MouseMoveEventArgs = OpenTK.Platform.MouseMoveEventArgs;
using TK = OpenTK.Platform.Toolkit; using TK = OpenTK.Platform.Toolkit;
using Vector4 = System.Numerics.Vector4;
TK.Init(new ToolkitOptions() TK.Init(new ToolkitOptions()
{ {
@@ -51,24 +46,18 @@ Application app = new Pal2Application()
} }
}; };
PhysicalWindow window;
CancellationTokenSource source = new CancellationTokenSource(); CancellationTokenSource source = new CancellationTokenSource();
// GLEngine engine;
// ContextExecutor executor;
// DimUI dimUI;
Vector2 mousePos = Vector2.Zero;
Random r = new Random();
List<Vector3> points = new List<Vector3>();
// IFont font;
StringBuilder builder = new StringBuilder();
app.Initialize(); app.Initialize();
app.ExtensionRequire<StbImageLoader>(); app.ExtensionRequire<StbImageLoader>();
app.ExtensionLoad(new BlurgTextExtension(new BlurgTextExtensionFactory())); app.ExtensionLoad(new BlurgTextExtension(new BlurgTextExtensionFactory()));
window = (PhysicalWindow)app.CreatePhysicalWindow();
window.Title = "DashTerm"; PhysicalWindow window = (PhysicalWindow)app.CreatePhysicalWindow();
MessageBox box = MessageBox.Create(window, "Are you sure you want to exit?", "Confirm Exit", MessageBoxIcon.Question,
MessageBoxButtons.YesNo);
// window.Title = "DashTerm";
TK.Window.SetMinClientSize(window.WindowHandle, 300, 200); TK.Window.SetMinClientSize(window.WindowHandle, 300, 200);
TK.Window.SetClientSize(window.WindowHandle, new Vector2i(320, 240)); TK.Window.SetClientSize(window.WindowHandle, new Vector2i(320, 240));
TK.Window.SetBorderStyle(window.WindowHandle, WindowBorderStyle.ResizableBorder); TK.Window.SetBorderStyle(window.WindowHandle, WindowBorderStyle.ResizableBorder);
@@ -79,121 +68,25 @@ GLDeviceContext context = (GLDeviceContext)window.DeviceContext;
context.GLContext.MakeCurrent(); context.GLContext.MakeCurrent();
context.GLContext.SwapGroup.SwapInterval = 1; context.GLContext.SwapGroup.SwapInterval = 1;
// engine = new GLEngine(); GL.Disable(EnableCap.DepthTest);
// engine.Initialize(); GL.Enable(EnableCap.Blend);
// GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);
// executor = engine.GetExecutor(context); GL.ColorMask(true, true, true, true);
//
// dimUI = new DimUI(new DimUIConfig()
// {
// Font = new NamedFont("Noto Sans", 9f),
// });
EventQueue.EventRaised += (handle, type, eventArgs) =>
{
if (handle != window.WindowHandle)
return;
switch (type)
{
case PlatformEventType.Close:
source.Cancel();
break;
case PlatformEventType.MouseMove:
mousePos = ((MouseMoveEventArgs)eventArgs).ClientPosition;
break;
}
};
TK.Window.SetMode(window.WindowHandle, WindowMode.Normal); TK.Window.SetMode(window.WindowHandle, WindowMode.Normal);
// font = Typesetter.LoadFont("Nimbus Mono", 12f);
foreach (string str in typeof(Image).Assembly.GetManifestResourceNames()) Console.WriteLine(str);
BlurgFont font = context.ExtensionRequire<BlurgDcExtension>().Blurg BlurgFont font = context.ExtensionRequire<BlurgDcExtension>().Blurg
.QueryFont("Recursive Mono", FontWeight.Regular, false) ?? throw new Exception("Font not found"); .QueryFont("Rec Mono Linear", FontWeight.Regular, false) ?? throw new Exception("Font not found");
window.EventRaised += (sender, ea) => { window.EventRaised += (sender, ea) => {
if (ea is not PaintEventArgs) if (ea is not PaintEventArgs paint)
return; return;
TK.Window.GetSize(window.WindowHandle, out Vector2i size); paint.DeviceContext.ExtensionRequire<IDeviceContextBase>().ScaleOverride = 1.5f;
// executor.BeginFrame();
//
// dimUI.Begin(new Box2d(0, 0, size.X, size.Y), window.DrawQueue);
// dimUI.Text("Hello World!");
// dimUI.Button("Cancel"); dimUI.SameLine();
// dimUI.Button("OK");
//
// dimUI.Input("type me!", builder);
//
// 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, size.X, size.Y);
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.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);
GL.ColorMask(true, true, true, true);
ITexture texture = MessageBox.s_questionIcon.InternTexture(context); ITexture texture = MessageBox.s_questionIcon.InternTexture(context);
// executor.Draw(window.DrawQueue, new RectangleF(0, 0, size.X, size.Y), 1.5f /*(window as IDpiAwareWindow)?.Scale ?? 1*/);
// executor.EndFrame();
context.Begin(); 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));
BlurgDcExtension blurg = context.ExtensionRequire<BlurgDcExtension>(); BlurgDcExtension blurg = context.ExtensionRequire<BlurgDcExtension>();
blurg.DrawBlurgFormattedText(new BlurgFormattedText("Hello world!", font) { DefaultSize = 64f }, new System.Numerics.Vector3(24, 24, 1)); blurg.DrawBlurgFormattedText(new BlurgFormattedText("Hello world!", font) { DefaultSize = 64f }, new System.Numerics.Vector3(24, 24, 1));