157 lines
4.3 KiB
C#
157 lines
4.3 KiB
C#
using System;
|
|
using System.Collections.ObjectModel;
|
|
|
|
namespace Ghost.Editor.View.Controls.Docking;
|
|
|
|
/// <summary>
|
|
/// Base class for containers that can hold other dock modules.
|
|
/// </summary>
|
|
public abstract class DockContainer : DockModule
|
|
{
|
|
private readonly ObservableCollection<DockModule> _children = new();
|
|
protected bool _isCleaningUp;
|
|
/// <summary>
|
|
/// Gets the collection of child modules.
|
|
/// </summary>
|
|
public ReadOnlyObservableCollection<DockModule> Children { get; }
|
|
|
|
protected DockContainer()
|
|
{
|
|
Children = new ReadOnlyObservableCollection<DockModule>(_children);
|
|
_children.CollectionChanged += OnChildrenChanged;
|
|
}
|
|
|
|
private void OnChildrenChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
|
|
{
|
|
OnChildrenUpdated();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds a child module to the end of the container.
|
|
/// </summary>
|
|
/// <param name="module">The module to add.</param>
|
|
public virtual void AddChild(DockModule module)
|
|
{
|
|
InsertChild(_children.Count, module);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Inserts a child module at the specified index.
|
|
/// </summary>
|
|
/// <param name="index">The zero-based index at which the module should be inserted.</param>
|
|
/// <param name="module">The module to insert.</param>
|
|
public virtual void InsertChild(int index, DockModule module)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(module);
|
|
|
|
if (index < 0 || index > _children.Count)
|
|
throw new ArgumentOutOfRangeException(nameof(index));
|
|
|
|
if (module == this)
|
|
throw new ArgumentException("Cannot add a container to itself.", nameof(module));
|
|
|
|
if (module is DockContainer container)
|
|
{
|
|
var current = Owner;
|
|
while (current != null)
|
|
{
|
|
if (current == container)
|
|
throw new ArgumentException("Cannot add a container that is an ancestor of this container.", nameof(module));
|
|
current = current.Owner;
|
|
}
|
|
}
|
|
|
|
if (_children.Contains(module))
|
|
return;
|
|
|
|
module.Owner?.RemoveChild(module);
|
|
module.Owner = this;
|
|
module.Root = Root;
|
|
_children.Insert(index, module);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes a child module from the container.
|
|
/// </summary>
|
|
/// <param name="module">The module to remove.</param>
|
|
public virtual void RemoveChild(DockModule module)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(module);
|
|
|
|
if (_children.Remove(module))
|
|
{
|
|
module.Owner = null;
|
|
module.Root = null;
|
|
if (!_isCleaningUp)
|
|
{
|
|
CheckCleanup();
|
|
}
|
|
}
|
|
}
|
|
|
|
public virtual void ReplaceChild(DockModule oldChild, DockModule newChild)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(oldChild);
|
|
ArgumentNullException.ThrowIfNull(newChild);
|
|
int index = _children.IndexOf(oldChild);
|
|
if (index < 0) throw new ArgumentException("oldChild not found");
|
|
|
|
// Detach newChild from its current owner if any
|
|
newChild.Owner?.RemoveChild(newChild);
|
|
|
|
// Remove oldChild without triggering cleanup
|
|
_isCleaningUp = true;
|
|
try
|
|
{
|
|
_children.RemoveAt(index);
|
|
oldChild.Owner = null;
|
|
oldChild.Root = null;
|
|
|
|
newChild.Owner = this;
|
|
newChild.Root = Root;
|
|
_children.Insert(index, newChild);
|
|
}
|
|
finally
|
|
{
|
|
_isCleaningUp = false;
|
|
}
|
|
OnChildrenUpdated();
|
|
CheckCleanup();
|
|
}
|
|
|
|
protected virtual void CheckCleanup()
|
|
{
|
|
if (_children.Count == 0)
|
|
{
|
|
Owner?.RemoveChild(this);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes all child modules from the container.
|
|
/// </summary>
|
|
public void Clear()
|
|
{
|
|
foreach (var child in _children)
|
|
{
|
|
child.Owner = null;
|
|
child.Root = null;
|
|
}
|
|
_children.Clear();
|
|
if (!_isCleaningUp)
|
|
{
|
|
CheckCleanup();
|
|
}
|
|
}
|
|
|
|
protected override void OnRootChanged()
|
|
{
|
|
foreach (var child in _children)
|
|
{
|
|
child.Root = Root;
|
|
}
|
|
}
|
|
|
|
protected virtual void OnChildrenUpdated() { }
|
|
}
|