diff --git a/Dashboard.Common/Drawing/IDeviceContextBase.cs b/Dashboard.Common/Drawing/IDeviceContextBase.cs
index 1aa7b1c..6d98d24 100644
--- a/Dashboard.Common/Drawing/IDeviceContextBase.cs
+++ b/Dashboard.Common/Drawing/IDeviceContextBase.cs
@@ -6,18 +6,18 @@ namespace Dashboard.Drawing
{
public interface IDeviceContextBase : IDeviceContextExtension
{
- RectangleF ClipRegion { get; }
- RectangleF ScissorRegion { get; }
+ Box2d ClipRegion { get; }
+ Box2d ScissorRegion { get; }
Matrix4x4 Transforms { get; }
float Scale { get; }
float ScaleOverride { get; set; }
void ResetClip();
- void PushClip(RectangleF clipRegion);
+ void PushClip(Box2d clipRegion);
void PopClip();
void ResetScissor();
- void PushScissor(RectangleF scissorRegion);
+ void PushScissor(Box2d scissorRegion);
void PopScissor();
void ResetTransforms();
diff --git a/Dashboard.Common/Events/KeyboardEvents.cs b/Dashboard.Common/Events/KeyboardEvents.cs
index 345881e..577a7d8 100644
--- a/Dashboard.Common/Events/KeyboardEvents.cs
+++ b/Dashboard.Common/Events/KeyboardEvents.cs
@@ -44,4 +44,16 @@ namespace Dashboard.Events
public ScanCode ScanCode { get; } = scanCode;
public ModifierKeys ModifierKeys { get; } = modifierKeys;
}
+
+ public class TextInputEventArgs(string text) : UiEventArgs(UiEventType.TextEdit)
+ {
+ public string Text { get; } = text;
+ }
+
+ public class TextEditEventArgs(string candidate, int cursor, int length) : UiEventArgs(UiEventType.TextEdit)
+ {
+ public string Candidate { get; } = candidate;
+ public int Cursor { get; } = cursor;
+ public int Length { get; } = length;
+ }
}
diff --git a/Dashboard.Common/Events/UiEventArgs.cs b/Dashboard.Common/Events/UiEventArgs.cs
index 43eedfc..a9728d7 100644
--- a/Dashboard.Common/Events/UiEventArgs.cs
+++ b/Dashboard.Common/Events/UiEventArgs.cs
@@ -76,8 +76,7 @@ namespace Dashboard.Events
}
}
- public class ControlResizedEventArgs
+ public class ResizeEventArgs() : UiEventArgs(UiEventType.ControlResized)
{
-
}
}
diff --git a/Dashboard.Common/Windowing/IDeviceContext.cs b/Dashboard.Common/Windowing/IDeviceContext.cs
index 64d7e1c..799221a 100644
--- a/Dashboard.Common/Windowing/IDeviceContext.cs
+++ b/Dashboard.Common/Windowing/IDeviceContext.cs
@@ -1,4 +1,5 @@
using System.Drawing;
+using System.Numerics;
namespace Dashboard.Windowing
{
@@ -15,6 +16,6 @@ namespace Dashboard.Windowing
///
/// The size of the window framebuffer in pixels.
///
- Size FramebufferSize { get; }
+ Vector2 FramebufferSize { get; }
}
-}
\ No newline at end of file
+}
diff --git a/Dashboard.OpenGL/Drawing/DeviceContextBase.cs b/Dashboard.OpenGL/Drawing/DeviceContextBase.cs
index da2d8f2..ea58e0c 100644
--- a/Dashboard.OpenGL/Drawing/DeviceContextBase.cs
+++ b/Dashboard.OpenGL/Drawing/DeviceContextBase.cs
@@ -7,14 +7,15 @@ using OpenTK.Graphics.OpenGL;
using OpenTK.Graphics.Wgl;
using OpenTK.Mathematics;
using ColorBuffer = OpenTK.Graphics.OpenGL.ColorBuffer;
+using Vector2 = System.Numerics.Vector2;
namespace Dashboard.OpenGL.Drawing
{
public class DeviceContextBase : IDeviceContextBase
{
private readonly Stack _transforms = new Stack();
- private readonly Stack _clipRegions = new Stack();
- private readonly Stack _scissorRegions = new Stack();
+ private readonly Stack _clipRegions = new Stack();
+ private readonly Stack _scissorRegions = new Stack();
public DeviceContext Context { get; private set; } = null!;
IContextBase IContextExtensionBase.Context => Context;
@@ -22,8 +23,8 @@ namespace Dashboard.OpenGL.Drawing
public string DriverVendor => "Dashboard";
public Version DriverVersion => new Version(0, 1);
- public RectangleF ClipRegion => _clipRegions.Peek();
- public RectangleF ScissorRegion => _scissorRegions.Peek();
+ public Box2d ClipRegion => _clipRegions.Peek();
+ public Box2d ScissorRegion => _scissorRegions.Peek();
public Matrix4x4 Transforms => _transforms.Peek();
public float Scale => ScaleOverride > 0 ? ScaleOverride : (Context.Window as IDpiAwareWindow)?.Scale ?? 1;
public float ScaleOverride { get; set; } = -1f;
@@ -50,17 +51,17 @@ namespace Dashboard.OpenGL.Drawing
{
_clipRegions.Clear();
- SizeF size = ((GLDeviceContext)Context).GLContext.FramebufferSize;
- _clipRegions.Push(new RectangleF(0,0, size.Width, size.Height));
+ Vector2 size = ((GLDeviceContext)Context).GLContext.FramebufferSize;
+ _clipRegions.Push(new Box2d(Vector2.Zero, size));
SetClip(ClipRegion);
}
- public void PushClip(RectangleF clipRegion)
+ public void PushClip(Box2d 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));
+ clipRegion = new Box2d(ClipRegion.Min.X + clipRegion.Min.X, ClipRegion.Min.Y + clipRegion.Min.Y,
+ Math.Min(ClipRegion.Max.X, ClipRegion.Min.X + clipRegion.Max.X),
+ Math.Min(ClipRegion.Max.Y, ClipRegion.Max.Y + clipRegion.Max.Y));
_clipRegions.Push(clipRegion);
SetClip(clipRegion);
@@ -76,11 +77,11 @@ namespace Dashboard.OpenGL.Drawing
{
GL.Disable(EnableCap.ScissorTest);
_scissorRegions.Clear();
- SizeF size = ((GLDeviceContext)Context).GLContext.FramebufferSize;
- _scissorRegions.Push(new RectangleF(0,0, size.Width, size.Height));
+ Vector2 size = ((GLDeviceContext)Context).GLContext.FramebufferSize;
+ _scissorRegions.Push(new Box2d(Vector2.Zero, size));
}
- public void PushScissor(RectangleF scissorRegion)
+ public void PushScissor(Box2d scissorRegion)
{
GL.Enable(EnableCap.ScissorTest);
@@ -101,30 +102,30 @@ namespace Dashboard.OpenGL.Drawing
SetScissor(ClipRegion);
}
- private void SetClip(RectangleF rect)
+ private void SetClip(Box2d rect)
{
- SizeF size = ((GLDeviceContext)Context).GLContext.FramebufferSize;
+ Vector2 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));
+ (int)Math.Round(rect.Min.X),
+ (int)Math.Round(size.Y - rect.Min.Y - rect.Size.Y),
+ (int)Math.Round(rect.Size.X),
+ (int)Math.Round(rect.Size.Y));
}
- void SetScissor(RectangleF rect)
+ void SetScissor(Box2d rect)
{
- SizeF size = ((GLDeviceContext)Context).GLContext.FramebufferSize;
+ Vector2 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));
+ (int)Math.Round(rect.Min.X),
+ (int)Math.Round(size.Y - rect.Min.Y - rect.Size.Y),
+ (int)Math.Round(rect.Size.X),
+ (int)Math.Round(rect.Size.Y));
}
public void ResetTransforms()
{
- SizeF size = ((GLDeviceContext)Context).GLContext.FramebufferSize;
- Matrix4x4 m = Matrix4x4.CreateOrthographicOffCenterLeftHanded(0, size.Width, size.Height, 0, 1, -1);
+ Vector2 size = ((GLDeviceContext)Context).GLContext.FramebufferSize;
+ Matrix4x4 m = Matrix4x4.CreateOrthographicOffCenterLeftHanded(0, size.X, size.Y, 0, 1, -1);
_transforms.Clear();
_transforms.Push(m);
diff --git a/Dashboard.OpenGL/Drawing/ImmediateMode.cs b/Dashboard.OpenGL/Drawing/ImmediateMode.cs
index e32c48d..4c7f181 100644
--- a/Dashboard.OpenGL/Drawing/ImmediateMode.cs
+++ b/Dashboard.OpenGL/Drawing/ImmediateMode.cs
@@ -110,7 +110,6 @@ 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 = Context.ExtensionRequire().Transforms;
GL.UseProgram(_program);
@@ -150,7 +149,6 @@ 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 = Context.ExtensionRequire().Transforms;
GL.UseProgram(_program);
@@ -189,7 +187,6 @@ namespace Dashboard.OpenGL.Drawing
GL.EnableVertexAttribArray(_program_atexcoord);
GL.VertexAttribPointer(_program_acolor, 4, VertexAttribPointerType.Float, false, ImmediateVertex.Size, ImmediateVertex.ColorOffset);
GL.EnableVertexAttribArray(_program_acolor);
- Size size = ((GLDeviceContext)Context).GLContext.FramebufferSize;
Matrix4x4 view = Context.ExtensionRequire().Transforms;
GL.UseProgram(_program);
diff --git a/Dashboard.OpenGL/IGLContext.cs b/Dashboard.OpenGL/IGLContext.cs
index 9ab3b0a..27d9ae6 100644
--- a/Dashboard.OpenGL/IGLContext.cs
+++ b/Dashboard.OpenGL/IGLContext.cs
@@ -1,4 +1,4 @@
-using System.Drawing;
+using System.Numerics;
using Dashboard.Windowing;
namespace Dashboard.OpenGL
@@ -17,7 +17,7 @@ namespace Dashboard.OpenGL
///
/// The size of the framebuffer in pixels.
///
- public Size FramebufferSize { get; }
+ public Vector2 FramebufferSize { get; }
///
/// Called when the context is disposed.
diff --git a/Dashboard.OpenTK/PAL2/Pal2Application.cs b/Dashboard.OpenTK/PAL2/Pal2Application.cs
index 5f378c7..278377d 100644
--- a/Dashboard.OpenTK/PAL2/Pal2Application.cs
+++ b/Dashboard.OpenTK/PAL2/Pal2Application.cs
@@ -121,6 +121,7 @@ namespace Dashboard.OpenTK.PAL2
return;
}
break;
+ // Mouse Events
case PlatformEventType.MouseDown:
{
MouseButtonDownEventArgs down = (MouseButtonDownEventArgs)args;
@@ -161,6 +162,8 @@ namespace Dashboard.OpenTK.PAL2
info.Window.SendEvent(this, scroll2);
break;
}
+
+ // Keyboard & Text Events
case PlatformEventType.KeyDown:
{
KeyDownEventArgs down = (KeyDownEventArgs)args;
@@ -185,11 +188,43 @@ namespace Dashboard.OpenTK.PAL2
info.Window.SendEvent(this, up2);
break;
}
+
+ case PlatformEventType.TextInput:
+ {
+ OPENTK.TextInputEventArgs textInput = (OPENTK.TextInputEventArgs)args;
+ DB.TextInputEventArgs textInput2 = new DB.TextInputEventArgs(textInput.Text);
+ info.Window.SendEvent(this, textInput2);
+ break;
+ }
+ case PlatformEventType.TextEditing:
+ {
+ TextEditingEventArgs textEditing = (TextEditingEventArgs)args;
+ TextEditEventArgs textEditing2 = new TextEditEventArgs(textEditing.Candidate, textEditing.Cursor, textEditing.Length);
+ info.Window.SendEvent(this, textEditing2);
+ break;
+ }
+
+ // Window/Surface related events.
case PlatformEventType.Close:
{
info.Window.SendEvent(this, new WindowCloseEvent());
break;
}
+ case PlatformEventType.WindowFramebufferResize:
+ {
+ var resize = (WindowFramebufferResizeEventArgs)args;
+ info.Window.SendEvent(this, new ResizeEventArgs());
+ info.Window.SendEvent(this, new PaintEventArgs(info.Window.DeviceContext));
+ break;
+ }
+ case PlatformEventType.WindowResize:
+ {
+ var resize = (WindowResizeEventArgs)args;
+ info.Window.SendEvent(this, new ResizeEventArgs());
+ info.Window.SendEvent(this, new PaintEventArgs(info.Window.DeviceContext));
+ break;
+ }
+
default:
Debugger?.LogDebug($"Unknown event type {type} with \"{args}\".");
break;
diff --git a/Dashboard.OpenTK/PAL2/Pal2GLContext.cs b/Dashboard.OpenTK/PAL2/Pal2GLContext.cs
index 7b8f7ca..ba825a1 100644
--- a/Dashboard.OpenTK/PAL2/Pal2GLContext.cs
+++ b/Dashboard.OpenTK/PAL2/Pal2GLContext.cs
@@ -15,14 +15,15 @@ namespace Dashboard.OpenTK.PAL2
public WindowHandle WindowHandle { get; }
public ISwapGroup SwapGroup { get; }
+
public int ContextGroup { get; }
- public Size FramebufferSize
+ public System.Numerics.Vector2 FramebufferSize
{
get
{
TK.Window.GetFramebufferSize(WindowHandle, out Vector2i size);
- return new Size(size.X, size.Y);
+ return new System.Numerics.Vector2(size.X, size.Y);
}
}
@@ -64,6 +65,7 @@ namespace Dashboard.OpenTK.PAL2
private static int _contextGroupId = 0;
private static ConcurrentDictionary _contextGroupRootContexts = new ConcurrentDictionary();
+ private Size _framebufferSize;
private static int GetContextGroup(OpenGLContextHandle handle)
{
diff --git a/Dashboard/Controls/Button.cs b/Dashboard/Controls/Button.cs
new file mode 100644
index 0000000..1a0db9d
--- /dev/null
+++ b/Dashboard/Controls/Button.cs
@@ -0,0 +1,67 @@
+using System;
+using System.Drawing;
+using System.Numerics;
+using Dashboard.Drawing;
+using Dashboard.Pal;
+
+namespace Dashboard.Controls
+{
+ public class Button : Control
+ {
+ private Vector2 _intrinsicSize = Vector2.Zero;
+
+ public bool AutoSize { get; set; } = true;
+ public string Text { get; set; } = "Click!";
+
+ public Font Font { get; set; } = Font.Create(new FontInfo("Rec Mono Linear"));
+ public float TextSize { get; set; } = 12f;
+ public Brush TextBrush { get; set; } = new SolidColorBrush(Color.Black);
+ public Brush ButtonBrush { get; set; } = new SolidColorBrush(Color.DarkSlateGray);
+
+ public Vector2 Padding { get; set; } = new Vector2(4, 4);
+
+ public event EventHandler? Clicked;
+
+ public override Vector2 CalculateIntrinsicSize()
+ {
+ return _intrinsicSize + 2 * Padding;
+ }
+
+ protected void CalculateSize(DeviceContext dc)
+ {
+ Box2d box = dc.ExtensionRequire().MeasureText(Font.Base, TextSize, Text);
+ _intrinsicSize = box.Size;
+ Size = box.Size;
+ ClientArea = new Box2d(ClientArea.Min, ClientArea.Min + Size);
+ }
+
+ public override void OnPaint(DeviceContext dc)
+ {
+ base.OnPaint(dc);
+
+ if (AutoSize)
+ CalculateSize(dc);
+
+ var dcb = dc.ExtensionRequire();
+ if (HideOverflow)
+ dcb.PushScissor(ClientArea);
+
+ dcb.PushTransforms(Matrix4x4.CreateTranslation(ClientArea.Left, ClientArea.Top, 0));
+
+ var imm = dc.ExtensionRequire();
+ Color color = (ButtonBrush as SolidColorBrush)?.Color ?? Color.Black;
+ Vector4 colorVector = new Vector4(color.R / 255f, color.G / 255f, color.B / 255f, color.A / 255f);
+ imm.Rectangle(ClientArea, 0, colorVector);
+
+ var text = dc.ExtensionRequire();
+ color = (TextBrush as SolidColorBrush)?.Color ?? Color.Black;
+ 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();
+ }
+ }
+}
diff --git a/Dashboard/Controls/Container.cs b/Dashboard/Controls/Container.cs
index 14a7161..cd9cc0d 100644
--- a/Dashboard/Controls/Container.cs
+++ b/Dashboard/Controls/Container.cs
@@ -41,7 +41,7 @@ namespace Dashboard.Controls
base.OnPaint(dc);
var dcb = dc.ExtensionRequire();
- dcb.PushClip(new RectangleF(ClientArea.Left, ClientArea.Bottom, ClientArea.Size.X, ClientArea.Size.Y));
+ dcb.PushClip(ClientArea);
// dcb.PushTransforms(Matrix4x4.CreateTranslation(ClientArea.Left, ClientArea.Bottom, 0));
LayoutChildren();
diff --git a/Dashboard/Controls/Control.cs b/Dashboard/Controls/Control.cs
index 7eed965..ea91625 100644
--- a/Dashboard/Controls/Control.cs
+++ b/Dashboard/Controls/Control.cs
@@ -72,6 +72,11 @@ namespace Dashboard.Controls
public event EventHandler? Disposing;
public event EventHandler? Resized;
+ public virtual Vector2 CalculateIntrinsicSize()
+ {
+ return Vector2.Max(Vector2.Zero, Vector2.Max(Size, MinimumSize));
+ }
+
public virtual void OnPaint(DeviceContext dc)
{
Painting?.Invoke(this, dc);
diff --git a/Dashboard/Controls/ImageBox.cs b/Dashboard/Controls/ImageBox.cs
new file mode 100644
index 0000000..d27914d
--- /dev/null
+++ b/Dashboard/Controls/ImageBox.cs
@@ -0,0 +1,27 @@
+using System.Numerics;
+using Dashboard.Drawing;
+using Dashboard.Pal;
+
+namespace Dashboard.Controls
+{
+ public class ImageBox : Control
+ {
+ public Image? Image { get; set; }
+
+ public override Vector2 CalculateIntrinsicSize()
+ {
+ return new Vector2(Image?.Width ?? 0, Image?.Height ?? 0);
+ }
+
+ public override void OnPaint(DeviceContext dc)
+ {
+ if (Image == null)
+ return;
+
+ Size = CalculateIntrinsicSize();
+
+ dc.ExtensionRequire().Image(new Box2d(ClientArea.Min, ClientArea.Min + Size), new Box2d(0, 0, 1, 1), 0, Image.InternTexture(dc));
+ base.OnPaint(dc);
+ }
+ }
+}
diff --git a/Dashboard/Controls/Label.cs b/Dashboard/Controls/Label.cs
index 7b7a9d3..2e6c3c5 100644
--- a/Dashboard/Controls/Label.cs
+++ b/Dashboard/Controls/Label.cs
@@ -8,6 +8,8 @@ namespace Dashboard.Controls
{
public class Label : Control
{
+ private Vector2 _intrinsicSize = Vector2.Zero;
+
public bool AutoSize { get; set; } = true;
public string Text { get; set; } = "";
@@ -18,9 +20,15 @@ namespace Dashboard.Controls
public float TextSize { get; set; } = 12f;
public Brush TextBrush { get; set; } = new SolidColorBrush(Color.Black);
+ public override Vector2 CalculateIntrinsicSize()
+ {
+ return _intrinsicSize;
+ }
+
protected void CalculateSize(DeviceContext dc)
{
Box2d box = dc.ExtensionRequire().MeasureText(Font.Base, TextSize, Text);
+ _intrinsicSize = box.Size;
Size = box.Size;
ClientArea = new Box2d(ClientArea.Min, ClientArea.Min + Size);
}
@@ -34,7 +42,7 @@ namespace Dashboard.Controls
var dcb = dc.ExtensionRequire();
if (HideOverflow)
- dcb.PushScissor(new RectangleF(ClientArea.Left, ClientArea.Top, ClientArea.Size.X, ClientArea.Size.Y));
+ dcb.PushScissor(ClientArea);
dcb.PushTransforms(Matrix4x4.CreateTranslation(ClientArea.Left, ClientArea.Top, 0));
diff --git a/Dashboard/Controls/MessageBox.cs b/Dashboard/Controls/MessageBox.cs
index f9fc267..b37e989 100644
--- a/Dashboard/Controls/MessageBox.cs
+++ b/Dashboard/Controls/MessageBox.cs
@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Collections.ObjectModel;
+using System.Collections.Specialized;
using System.IO;
using System.Reflection;
using Dashboard.Drawing;
@@ -35,12 +36,47 @@ namespace Dashboard.Controls
///
public class MessageBox : Form
{
- private Image? _icon;
+ private MessageBoxIcon _icon;
+ private MessageBoxButtons _buttons;
+ private ImageBox _iconBox = new ImageBox();
private Label _label = new Label();
- private readonly List