using Dashboard.Collections; using Dashboard.Windowing; using BindingFlags = System.Reflection.BindingFlags; namespace Dashboard.Pal { public abstract class Application : IContextBase { public abstract string DriverName { get; } public abstract string DriverVendor { get; } public abstract Version DriverVersion { get; } public virtual string AppTitle { get; set; } = "Dashboard Application"; public bool IsInitialized { get; private set; } = false; 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); public event EventHandler? DeviceContextCreated; public Application() { Current = this; } ~Application() { InvokeDispose(false); } public void Initialize() { if (IsInitialized) return; IsInitialized = true; InitializeInternal(); } protected virtual void InitializeInternal() { } protected internal virtual void OnDeviceContextCreated(DeviceContext dc) { DeviceContextCreated?.Invoke(this, dc); } public virtual void RunEvents(bool wait) { if (!IsInitialized) Initialize(); } public void Run() => Run(true, CancellationToken.None); public void Run(bool wait) => Run(wait, CancellationToken.None); public void Run(bool waitForEvents, CancellationToken token) { InitializeInternal(); while (!token.IsCancellationRequested) { RunEvents(waitForEvents); } } #region Window API /// /// Creates a window. It could be a virtual window, or a physical window. /// /// A window. public abstract IWindow CreateWindow(); /// /// Always creates a physical window. /// /// A physical window. public abstract IPhysicalWindow CreatePhysicalWindow(); /// /// Create a physical window with a window manager. /// /// A physical window with the given window manager. public IPhysicalWindow CreatePhysicalWindow(IWindowManager wm) { IPhysicalWindow window = CreatePhysicalWindow(); 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 : IApplicationExtension { return _extensions.Contains() || _preloadedExtensions.Contains(); } public bool ExtensionPreload(Func loader) where T : IApplicationExtension { return _preloadedExtensions.Add(loader); } public bool ExtensionPreload() where T : IApplicationExtension, new() { return _preloadedExtensions.Add(() => new T()); } public T ExtensionRequire() where T : IApplicationExtension { T? extension = default; if (_extensions.TryGet(out extension)) return extension; lock (_extensions) { if (_extensions.TryGet(out extension)) return extension; if (_preloadedExtensions.Remove(out Func? loader)) { extension = (T)loader!(); } else { extension = Activator.CreateInstance(); } _extensions.Add(extension); extension.Require(this); } return extension; } public bool ExtensionLoad(T instance) where T : IApplicationExtension { if (_extensions.Contains(instance)) return false; _extensions.Add(instance); instance.Require(this); return true; } protected virtual void Dispose(bool isDisposing) { if (!isDisposing) return; foreach (IApplicationExtension extension in _extensions) { extension.Dispose(); } GC.SuppressFinalize(this); } private void InvokeDispose(bool isDisposing) { if (IsDisposed) return; IsDisposed = true; Dispose(isDisposing); } 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; } } }