Implement Control ownership and nice to have interfaces.

This commit is contained in:
2026-09-07 22:40:58 +03:00
parent e46a13249b
commit 1100fac94b
3 changed files with 129 additions and 47 deletions
+64 -9
View File
@@ -1,6 +1,8 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using Dashboard.Drawing; using Dashboard.Drawing;
using Dashboard.Events; using Dashboard.Events;
using Dashboard.Layout; using Dashboard.Layout;
@@ -8,21 +10,21 @@ using Dashboard.Pal;
namespace Dashboard.Controls namespace Dashboard.Controls
{ {
public class Container : Control, IList<Control>, ILayoutContainer public class Container : Control, IList<Control>, ILayoutContainer, INotifyCollectionChanged
{ {
private readonly List<Control> _controls = new List<Control>(); private readonly List<Control> _controls = new List<Control>();
public ContainerLayoutInfo ContainerLayout { get; } = new ContainerLayoutInfo();
public int Count => _controls.Count; public int Count => _controls.Count;
public bool IsReadOnly => false; public bool IsReadOnly => false;
public ContainerLayoutInfo ContainerLayout { get; } = new ContainerLayoutInfo();
ILayoutItem IReadOnlyList<ILayoutItem>.this[int index] => _controls[index]; ILayoutItem IReadOnlyList<ILayoutItem>.this[int index] => _controls[index];
public event EventHandler<ContainerChildAddedEventArgs>? ChildAdded; public event EventHandler<ContainerChildAddedEventArgs>? ChildAdded;
public event EventHandler<ContainerChildRemovedEventArgs>? ChildRemoved; public event EventHandler<ContainerChildRemovedEventArgs>? ChildRemoved;
public event NotifyCollectionChangedEventHandler? CollectionChanged;
public Control this[int index] public Control this[int index]
{ {
@@ -30,6 +32,11 @@ namespace Dashboard.Controls
set => _controls[index] = value; set => _controls[index] = value;
} }
public Container()
{
ContainerLayout.PropertyChanged += OnContainerLayoutChanged;
}
protected override void ValidateLayout() protected override void ValidateLayout()
{ {
if (!IsLayoutEnabled || IsLayoutValid) if (!IsLayoutEnabled || IsLayoutValid)
@@ -82,16 +89,17 @@ namespace Dashboard.Controls
SetParent(this, item); SetParent(this, item);
_controls.Add(item); _controls.Add(item);
ChildAdded?.Invoke(this, new ContainerChildAddedEventArgs(this, item)); OnChildAdded(item);
} }
public void Clear() public void Clear()
{ {
foreach (Control control in this) foreach (Control control in this)
{ {
ChildRemoved?.Invoke(this, new ContainerChildRemovedEventArgs(this, control)); OnChildRemoved(control, false);
} }
_controls.Clear(); _controls.Clear();
OnCollectionChanged(NotifyCollectionChangedAction.Reset);
} }
public bool Contains(Control item) public bool Contains(Control item)
@@ -109,7 +117,7 @@ namespace Dashboard.Controls
if (!_controls.Remove(item)) if (!_controls.Remove(item))
return false; return false;
ChildRemoved?.Invoke(this, new ContainerChildRemovedEventArgs(this, item)); OnChildRemoved(item);
return true; return true;
} }
@@ -123,17 +131,64 @@ namespace Dashboard.Controls
SetParent(this, item); SetParent(this, item);
_controls.Insert(index, item); _controls.Insert(index, item);
ChildAdded?.Invoke(this, new ContainerChildAddedEventArgs(this, item)); OnChildAdded(item);
} }
public void RemoveAt(int index) public void RemoveAt(int index)
{ {
Control child = _controls[index]; Control child = _controls[index];
_controls.RemoveAt(index); _controls.RemoveAt(index);
ChildRemoved?.Invoke(this, new ContainerChildRemovedEventArgs(this, child)); OnChildRemoved(child);
} }
protected override void OnOwnerChanged(Form value)
{
base.OnOwnerChanged(value);
foreach (Control control in this)
{
SetOwner(control, value);
}
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
Clear();
}
ContainerLayout.PropertyChanged -= OnContainerLayoutChanged;
}
protected virtual void OnChildAdded(Control item)
{
SetOwner(item, Owner ?? null);
ChildAdded?.Invoke(this, new ContainerChildAddedEventArgs(this, item));
OnCollectionChanged(NotifyCollectionChangedAction.Add);
}
protected virtual void OnChildRemoved(Control control, bool notifyCollectionChanged = true)
{
SetOwner(control, null!);
ChildRemoved?.Invoke(this, new ContainerChildRemovedEventArgs(this, control));
if (notifyCollectionChanged)
OnCollectionChanged(NotifyCollectionChangedAction.Remove);
}
protected virtual void OnCollectionChanged(NotifyCollectionChangedAction action)
{
CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(action));
}
private void OnContainerLayoutChanged(object? sender, PropertyChangedEventArgs ea)
{
OnPropertyChanged(nameof(ContainerLayout));
}
} }
public class ContainerChildAddedEventArgs(Container parent, Control child) : EventArgs public class ContainerChildAddedEventArgs(Container parent, Control child) : EventArgs
+44 -21
View File
@@ -13,27 +13,33 @@ namespace Dashboard.Controls
{ {
public class Control : IEventListener, ILayoutItem, IDisposable public class Control : IEventListener, ILayoutItem, IDisposable
{ {
private Form? _owner = null; public string? Id
{
public string? Id { get; set; } get;
set => SetField(ref field, value);
}
public Form Owner public Form Owner
{ {
get => _owner ?? throw NoOwnerException; get ;
protected set protected set
{ {
_owner = value;
OnOwnerChanged(value); OnOwnerChanged(value);
SetField(ref field, value);
} }
} } = null!;
public Control? Parent { get; private set; } = null; public Control? Parent
{
get;
private set => SetField(ref field, value);
} = null;
public bool Disposed { get; private set; } public bool Disposed { get; private set; }
public virtual Box2d ClientArea => Layout.ComputedBox; public virtual Box2d ClientArea => Layout.ComputedBox;
public bool IsFocused => _owner?.FocusedControl == this; public bool IsFocused => Owner?.FocusedControl == this;
public Brush Background { get; set; } = new SolidColorBrush(Color.Transparent); public Brush Background { get; set => SetField(ref field, value); } = new SolidColorBrush(Color.Transparent);
public Brush BorderBrush { get; set; } = new SolidColorBrush(Color.Black); public Brush BorderBrush { get; set => SetField(ref field, value); } = new SolidColorBrush(Color.Black);
public LayoutInfo Layout { get; } = new LayoutInfo(); public LayoutInfo Layout { get; } = new LayoutInfo();
public bool IsLayoutEnabled { get; private set; } = true; public bool IsLayoutEnabled { get; private set; } = true;
@@ -49,6 +55,11 @@ namespace Dashboard.Controls
public event EventHandler? Disposing; public event EventHandler? Disposing;
public event EventHandler? Resized; public event EventHandler? Resized;
public Control()
{
Layout.PropertyChanged += LayoutChanged;
}
public virtual Vector2 CalculateIntrinsicSize() public virtual Vector2 CalculateIntrinsicSize()
{ {
return Vector2.Zero; return Vector2.Zero;
@@ -74,7 +85,9 @@ namespace Dashboard.Controls
{ {
if (Disposed) if (Disposed)
return; return;
Disposed = true; Disposed = true;
Layout.PropertyChanged -= LayoutChanged;
Dispose(disposing); Dispose(disposing);
@@ -134,23 +147,13 @@ namespace Dashboard.Controls
FocusLost?.Invoke(sender, EventArgs.Empty); 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() protected virtual void OnResize()
{ {
Resized?.Invoke(this, EventArgs.Empty); Resized?.Invoke(this, EventArgs.Empty);
InvalidateLayout(); InvalidateLayout();
} }
private void OnOwnerChanged(Form value) protected virtual void OnOwnerChanged(Form value)
{ {
OwnerChanged?.Invoke(this, EventArgs.Empty); OwnerChanged?.Invoke(this, EventArgs.Empty);
} }
@@ -188,6 +191,26 @@ namespace Dashboard.Controls
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
} }
private void LayoutChanged(object? sender, PropertyChangedEventArgs ea)
{
OnPropertyChanged(nameof(Layout));
}
protected static Exception NoOwnerException => new Exception("No form owns this control"); protected static Exception NoOwnerException => new Exception("No form owns this control");
internal static void InvokeFocusGained(Form form, Control control)
{
control.OnFocusGained(form);
}
internal static void InvokeFocusLost(Form form, Control control)
{
control.OnFocusLost(form);
}
private protected static void SetOwner(Control item, Form owner)
{
item.Owner = owner;
}
} }
} }
+21 -17
View File
@@ -1,6 +1,5 @@
using System; using System;
using System.Drawing; using System.Drawing;
using System.Numerics;
using Dashboard.Drawing; using Dashboard.Drawing;
using Dashboard.Events; using Dashboard.Events;
using Dashboard.Pal; using Dashboard.Pal;
@@ -10,20 +9,18 @@ namespace Dashboard.Controls
{ {
public class Form : Container, IForm public class Form : Container, IForm
{ {
private string? _title = "Untitled Form";
public IWindow Window { get; } public IWindow Window { get; }
public Image? WindowIcon { get; set; } public Image? WindowIcon { get; set; }
public string? Title public string Title
{ {
get => _title; get;
set set
{ {
_title = value; field = value;
Window.Title = _title ?? ""; Window.Title = field ?? "";
}
} }
} = "Untitled Form";
public Control? FocusedControl { get; private set; } = null; public Control? FocusedControl { get; private set; } = null;
public override Box2d ClientArea public override Box2d ClientArea
@@ -37,19 +34,11 @@ namespace Dashboard.Controls
{ {
Window = window; Window = window;
Window.Form = this; Window.Form = this;
Window.Title = _title; Window.Title = Title;
Window.EventRaised += WindowEventRaised; Window.EventRaised += WindowEventRaised;
Background = new SolidColorBrush(Color.SlateGray); Background = new SolidColorBrush(Color.SlateGray);
} }
private void WindowEventRaised(object? sender, EventArgs e)
{
if (e is ResizeEventArgs)
{
InvalidateLayout();
}
}
public void Focus(Control control) public void Focus(Control control)
{ {
if (FocusedControl != null) if (FocusedControl != null)
@@ -108,5 +97,20 @@ namespace Dashboard.Controls
break; break;
} }
} }
protected override void OnChildAdded(Control item)
{
base.OnChildAdded(item);
SetOwner(item, this);
}
private void WindowEventRaised(object? sender, EventArgs e)
{
if (e is ResizeEventArgs)
{
InvalidateLayout();
}
}
} }
} }