using System; using System.ComponentModel; using System.Diagnostics; using System.Drawing; using System.Numerics; using Dashboard.Drawing; using Dashboard.Events; using Dashboard.Layout; using Dashboard.Pal; using Dashboard.Windowing; namespace Dashboard.Controls { public class Control : IEventListener, ILayoutItem, IDisposable { private Form? _owner = null; public string? Id { get; set; } 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 Box2d ClientArea { get; set; } public bool IsFocused => _owner?.FocusedControl == this; public Brush Background { get; set; } = new SolidColorBrush(Color.Transparent); public Brush BorderBrush { get; set; } = new SolidColorBrush(Color.Black); public LayoutInfo Layout { get; } = new LayoutInfo(); public bool IsLayoutEnabled { get; private set; } = true; protected bool IsLayoutValid { get; set; } = false; public event EventHandler? Painting; public event EventHandler? AnimationTick; public event EventHandler? OwnerChanged; public event EventHandler? ParentChanged; public event EventHandler? FocusGained; public event EventHandler? FocusLost; public event EventHandler? Disposing; public event EventHandler? Resized; public virtual Vector2 CalculateIntrinsicSize() { return Vector2.Zero; // return Vector2.Max(Vector2.Zero, Vector2.Max(Layout.Size, Layout.MinimumSize)); } public Vector2 CalculateSize(Vector2 limits) { return CalculateIntrinsicSize(); } public virtual void OnPaint(DeviceContext dc) { Painting?.Invoke(this, dc); } public virtual void OnAnimationTick(TickEventArgs tick) { AnimationTick?.Invoke(this, tick); } protected void InvokeDispose(bool disposing) { if (Disposed) return; Disposed = true; Dispose(disposing); if (disposing) GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (disposing) Disposing?.Invoke(this, EventArgs.Empty); } public void Dispose() => InvokeDispose(true); public event EventHandler? EventRaised; protected virtual void OnEventRaised(object? sender, EventArgs args) { switch (args) { case PaintEventArgs paint: OnPaint(paint.DeviceContext); break; } EventRaised?.Invoke(this, TransformEvent(sender, args)); } protected virtual EventArgs TransformEvent(object? sender, EventArgs args) { return args; } public void SendEvent(object? sender, EventArgs args) { OnEventRaised(sender, args); } internal static void SetParent(Container 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); InvalidateLayout(); } private void OnOwnerChanged(Form value) { OwnerChanged?.Invoke(this, EventArgs.Empty); } public void InvalidateLayout() { IsLayoutValid = false; } protected virtual void ValidateLayout() { IsLayoutValid = true; } public void ResumeLayout() { IsLayoutEnabled = true; } public void SuspendLayout() { IsLayoutEnabled = false; IsLayoutValid = false; } protected static Exception NoOwnerException => new Exception("No form owns this control"); public event PropertyChangedEventHandler? PropertyChanged; } }