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