diff --git a/Dashboard.Common/Pal/AppContext.cs b/Dashboard.Common/Pal/Application.cs similarity index 71% rename from Dashboard.Common/Pal/AppContext.cs rename to Dashboard.Common/Pal/Application.cs index 13990e7..2780248 100644 --- a/Dashboard.Common/Pal/AppContext.cs +++ b/Dashboard.Common/Pal/Application.cs @@ -4,7 +4,7 @@ using BindingFlags = System.Reflection.BindingFlags; namespace Dashboard.Pal { - public abstract class AppContext : IContextBase + public abstract class Application : IContextBase { public abstract string DriverName { get; } public abstract string DriverVendor { get; } @@ -16,12 +16,17 @@ namespace Dashboard.Pal public bool IsDisposed { get; private set; } = false; public IContextDebugger? Debugger { get; set; } - private readonly TypeDictionary _extensions = - new TypeDictionary(true); - private readonly TypeDictionary> _preloadedExtensions = - new TypeDictionary>(true); + private readonly TypeDictionary _extensions = + new TypeDictionary(true); + private readonly TypeDictionary> _preloadedExtensions = + new TypeDictionary>(true); - ~AppContext() + public Application() + { + Current = this; + } + + ~Application() { InvokeDispose(false); } @@ -83,19 +88,32 @@ namespace Dashboard.Pal window.WindowManager = wm; return window; } + + public IWindow CreateDialogWindow(IWindow? parent = null) + { + if (parent is IVirtualWindow virtualWindow) + { + IWindow? window = virtualWindow.WindowManager?.CreateWindow(); + + if (window != null) + return window; + } + + return CreatePhysicalWindow(); + } #endregion - public bool IsExtensionAvailable() where T : IAppContextExtension + public bool IsExtensionAvailable() where T : IApplicationExtension { return _extensions.Contains() || _preloadedExtensions.Contains(); } - public bool ExtensionPreload() where T : IAppContextExtension, new() + public bool ExtensionPreload() where T : IApplicationExtension, new() { return _preloadedExtensions.Add(() => new T()); } - public T ExtensionRequire() where T : IAppContextExtension + public T ExtensionRequire() where T : IApplicationExtension { T? extension = default; @@ -107,7 +125,7 @@ namespace Dashboard.Pal if (_extensions.TryGet(out extension)) return extension; - if (_preloadedExtensions.Remove(out Func? loader)) + if (_preloadedExtensions.Remove(out Func? loader)) { extension = (T)loader!(); } @@ -128,7 +146,7 @@ namespace Dashboard.Pal { if (!isDisposing) return; - foreach (IAppContextExtension extension in _extensions) + foreach (IApplicationExtension extension in _extensions) { extension.Dispose(); } @@ -145,5 +163,12 @@ namespace Dashboard.Pal } public void Dispose() => InvokeDispose(true); + + [ThreadStatic] private static Application _current; + public static Application Current + { + get => _current ?? throw new InvalidOperationException("There is currently no current application."); + set => _current = value; + } } } diff --git a/Dashboard.Common/Pal/DeviceContext.cs b/Dashboard.Common/Pal/DeviceContext.cs index fcfc1cd..f83adac 100644 --- a/Dashboard.Common/Pal/DeviceContext.cs +++ b/Dashboard.Common/Pal/DeviceContext.cs @@ -10,6 +10,9 @@ namespace Dashboard.Pal private readonly TypeDictionary> _preloadedExtensions = new TypeDictionary>(true); + private Dictionary _attributes = new Dictionary(); + + public abstract string DriverName { get; } public abstract string DriverVendor { get; } public abstract Version DriverVersion { get; } @@ -72,6 +75,29 @@ namespace Dashboard.Pal return extension; } + public void SetAttribute(string name, object? v) + { + if (v != null) + _attributes[name] = v; + else + _attributes.Remove(name); + } + + public void SetAttribute(string name, T v) => SetAttribute(name, (object?)v); + + public object? GetAttribute(string name) + { + return _attributes.GetValueOrDefault(name); + } + + public T? GetAttribute(string name) + { + object? o = GetAttribute(name); + if (o != null) + return (T?)o; + return default; + } + /// /// Implement your dispose in this function. /// diff --git a/Dashboard.Common/Pal/IAppContextExtension.cs b/Dashboard.Common/Pal/IAppContextExtension.cs deleted file mode 100644 index da27c13..0000000 --- a/Dashboard.Common/Pal/IAppContextExtension.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Dashboard.Pal -{ - public interface IAppContextExtension : IContextExtensionBase - { - - } -} diff --git a/Dashboard.Common/Pal/IApplicationExtension.cs b/Dashboard.Common/Pal/IApplicationExtension.cs new file mode 100644 index 0000000..bfab2dc --- /dev/null +++ b/Dashboard.Common/Pal/IApplicationExtension.cs @@ -0,0 +1,7 @@ +namespace Dashboard.Pal +{ + public interface IApplicationExtension : IContextExtensionBase + { + + } +} diff --git a/Dashboard.Common/Windowing/IWindow.cs b/Dashboard.Common/Windowing/IWindow.cs index bf733a7..55f6f19 100644 --- a/Dashboard.Common/Windowing/IWindow.cs +++ b/Dashboard.Common/Windowing/IWindow.cs @@ -50,6 +50,7 @@ namespace Dashboard.Windowing /// public interface IVirtualWindow : IWindow, IEventListener { + IWindowManager? WindowManager { get; set; } } /// diff --git a/Dashboard.OpenGL/Pal/GLTextureExtension.cs b/Dashboard.OpenGL/Pal/GLTextureExtension.cs index 2050b60..3c542e0 100644 --- a/Dashboard.OpenGL/Pal/GLTextureExtension.cs +++ b/Dashboard.OpenGL/Pal/GLTextureExtension.cs @@ -98,6 +98,11 @@ namespace Dashboard.Drawing.OpenGL.Pal return; } + if (levels == 0) + { + levels = Math.Max(Math.ILogB(width), Math.ILogB(height)); + } + Bind(); SizedInternalFormat glFormat = GetFormat(format); if (Extension.SupportsArbTextureStorage) @@ -122,18 +127,23 @@ namespace Dashboard.Drawing.OpenGL.Pal switch (Type) { case TextureType.Texture1D: - GL.TexImage1D(Target, 0, (InternalFormat)glFormat, width, 0, (OGL.PixelFormat)glFormat, PixelType.Byte, IntPtr.Zero); + GL.TexImage1D(Target, 0, (InternalFormat)glFormat, width, 0, (OGL.PixelFormat)glFormat, PixelType.UnsignedByte, IntPtr.Zero); break; case TextureType.Texture2D: - GL.TexImage2D(Target, 0, (InternalFormat)glFormat, width, height, 0, (OGL.PixelFormat)glFormat, PixelType.Byte, IntPtr.Zero); + GL.TexImage2D(Target, 0, (InternalFormat)glFormat, width, height, 0, (OGL.PixelFormat)glFormat, PixelType.UnsignedByte, IntPtr.Zero); break; case TextureType.Texture3D: case TextureType.Texture2DArray: case TextureType.Texture2DCube: - GL.TexImage3D(Target, 0, (InternalFormat)glFormat, width, height, depth, 0, (OGL.PixelFormat)glFormat, PixelType.Byte, IntPtr.Zero); + GL.TexImage3D(Target, 0, (InternalFormat)glFormat, width, height, depth, 0, (OGL.PixelFormat)glFormat, PixelType.UnsignedByte, IntPtr.Zero); break; } } + + Width = width; + Height = height; + Depth = depth; + Levels = levels; } public void Read(Span buffer, int level = 0, int align = 0) where T : unmanaged @@ -160,7 +170,7 @@ namespace Dashboard.Drawing.OpenGL.Pal PixelType glType = format switch { - PixelFormat.R8I or PixelFormat.Rg8I or PixelFormat.Rgb8I or PixelFormat.Rgba8I => PixelType.Byte, + PixelFormat.R8I or PixelFormat.Rg8I or PixelFormat.Rgb8I or PixelFormat.Rgba8I => PixelType.UnsignedByte, PixelFormat.R16F or PixelFormat.Rg16F or PixelFormat.Rgb16F or PixelFormat.Rgba16F => PixelType.HalfFloat, _ => throw new NotSupportedException() }; diff --git a/Dashboard.OpenTK/PAL2/Pal2AppContext.cs b/Dashboard.OpenTK/PAL2/Pal2Application.cs similarity index 93% rename from Dashboard.OpenTK/PAL2/Pal2AppContext.cs rename to Dashboard.OpenTK/PAL2/Pal2Application.cs index 2db51eb..e300b18 100644 --- a/Dashboard.OpenTK/PAL2/Pal2AppContext.cs +++ b/Dashboard.OpenTK/PAL2/Pal2Application.cs @@ -1,13 +1,13 @@ using Dashboard.Drawing.OpenGL.Pal; +using Dashboard.Pal; using Dashboard.Windowing; using OpenTK.Graphics; using OpenTK.Platform; -using AppContext = Dashboard.Pal.AppContext; using TK = OpenTK.Platform.Toolkit; namespace Dashboard.OpenTK.PAL2 { - public class Pal2AppContext : AppContext + public class Pal2Application : Application { public override string DriverName => "Dashboard OpenTK PAL2.0 Driver"; public override string DriverVendor => "Dashboard"; diff --git a/Dashboard/Controls/Control.cs b/Dashboard/Controls/Control.cs index 88fdfec..56b74ca 100644 --- a/Dashboard/Controls/Control.cs +++ b/Dashboard/Controls/Control.cs @@ -6,17 +6,30 @@ namespace Dashboard.Controls { public class Control : IEventListener, IDisposable { + private Form? _owner = null; + public string? Id { get; set; } public ClassSet Classes { get; } - public Form? Owner { get; protected set; } = null; + public Form Owner + { + get => _owner ?? throw NoOwnerException; + protected set + { + _owner = value; + OnOwnerChanged(value); + } + } + public Control? Parent { get; private set; } = null; public bool Disposed { get; private set; } - public virtual DrawQueue DrawQueue => Owner?.DrawQueue ?? throw NoOwnerException; public virtual Box2d ClientArea { get; set; } + public bool IsFocused => _owner?.FocusedControl == this; public event EventHandler? Painting; public event EventHandler? OwnerChanged; public event EventHandler? ParentChanged; + public event EventHandler? FocusGained; + public event EventHandler? FocusLost; public event EventHandler? Disposing; public event EventHandler? Resized; @@ -49,6 +62,7 @@ namespace Dashboard.Controls protected virtual void Dispose(bool disposing) { + if (disposing) Disposing?.Invoke(this, EventArgs.Empty); } public void Dispose() => InvokeDispose(true); @@ -61,6 +75,42 @@ namespace Dashboard.Controls internal static void SetParent(Control parent, Control child) { child.Parent = parent; + child.ParentChanged?.Invoke(child, EventArgs.Empty); + } + + public virtual void Focus() + { + (Owner ?? throw NoOwnerException).Focus(this); + } + + protected virtual void OnFocusGained(object sender) + { + FocusGained?.Invoke(sender, EventArgs.Empty); + } + + protected virtual void OnFocusLost(object sender) + { + FocusLost?.Invoke(sender, EventArgs.Empty); + } + + internal static void InvokeFocusGained(Form form, Control control) + { + control.OnFocusGained(form); + } + + internal static void InvokeFocusLost(Form form, Control control) + { + control.OnFocusLost(form); + } + + protected virtual void OnResize() + { + Resized?.Invoke(this, EventArgs.Empty); + } + + private void OnOwnerChanged(Form value) + { + OwnerChanged?.Invoke(this, EventArgs.Empty); } protected static Exception NoOwnerException => new Exception("No form owns this control"); diff --git a/Dashboard/Controls/Form.cs b/Dashboard/Controls/Form.cs index a25d57e..ff0e725 100644 --- a/Dashboard/Controls/Form.cs +++ b/Dashboard/Controls/Form.cs @@ -1,4 +1,6 @@ +using System; using Dashboard.Drawing; +using Dashboard.Resources; using Dashboard.Windowing; namespace Dashboard.Controls @@ -7,6 +9,10 @@ namespace Dashboard.Controls { public IWindow Window { get; } + public Image? WindowIcon { get; set; } + public string? Title { get; set; } = "Untitled Form"; + public Control? FocusedControl { get; private set; } = null; + public override Box2d ClientArea { get => new Box2d(0, 0, Window.ClientSize.Width, Window.ClientSize.Height); @@ -17,5 +23,15 @@ namespace Dashboard.Controls { Window = window; } + + + public void Focus(Control control) + { + if (FocusedControl != null) + InvokeFocusLost(this, FocusedControl); + + FocusedControl = control; + InvokeFocusGained(this, control); + } } } diff --git a/Dashboard/Controls/Label.cs b/Dashboard/Controls/Label.cs index 83fc447..1e217bc 100644 --- a/Dashboard/Controls/Label.cs +++ b/Dashboard/Controls/Label.cs @@ -30,7 +30,7 @@ namespace Dashboard.Controls public override void OnPaint() { base.OnPaint(); - DrawQueue.Text(new Vector3(ClientArea.Min, 0), TextBrush, Text, Font); + // DrawQueue.Text(new Vector3(ClientArea.Min, 0), TextBrush, Text, Font); } } } diff --git a/Dashboard/Controls/MessageBox.cs b/Dashboard/Controls/MessageBox.cs new file mode 100644 index 0000000..ea2e263 --- /dev/null +++ b/Dashboard/Controls/MessageBox.cs @@ -0,0 +1,138 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Collections.ObjectModel; +using System.IO; +using System.Reflection; +using Dashboard.Resources; +using Dashboard.Windowing; + +namespace Dashboard.Controls +{ + public enum MessageBoxIcon + { + Info, + Question, + Warning, + Error, + Custom, + } + + public enum MessageBoxButtons + { + AbortRetryIgnore, + CancelRetryContinue, + Ok, + OkCancel, + RetryCancel, + YesNo, + YesNoCancel, + Custom, + } + + /// + /// A simple message box dialog. + /// + public class MessageBox : Form + { + private Image? _icon; + private Label _label = new Label(); + private readonly List