using System; using System.Collections.ObjectModel; namespace Ghost.Editor.View.Controls.Docking; /// /// Base class for containers that can hold other dock modules. /// public abstract class DockContainer : DockModule { private readonly ObservableCollection _children = new(); protected bool _isCleaningUp; /// /// Gets the collection of child modules. /// public ReadOnlyObservableCollection Children { get; } protected DockContainer() { Children = new ReadOnlyObservableCollection(_children); _children.CollectionChanged += OnChildrenChanged; } private void OnChildrenChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { OnChildrenUpdated(); } /// /// Adds a child module to the end of the container. /// /// The module to add. public virtual void AddChild(DockModule module) { InsertChild(_children.Count, module); } /// /// Inserts a child module at the specified index. /// /// The zero-based index at which the module should be inserted. /// The module to insert. 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); } /// /// Removes a child module from the container. /// /// The module to remove. 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); } } /// /// Removes all child modules from the container. /// 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() { } }