fix(docking): improve structural integrity and add null validation

This commit is contained in:
2026-03-28 22:35:43 +09:00
parent e5aa328576
commit 1d48784a1c
3 changed files with 47 additions and 5 deletions

View File

@@ -9,6 +9,7 @@ namespace Ghost.Editor.View.Controls.Docking;
public abstract class DockContainer : DockModule public abstract class DockContainer : DockModule
{ {
private readonly ObservableCollection<DockModule> _children = new(); private readonly ObservableCollection<DockModule> _children = new();
protected bool _isCleaningUp;
/// <summary> /// <summary>
/// Gets the collection of child modules. /// Gets the collection of child modules.
/// </summary> /// </summary>
@@ -81,9 +82,42 @@ public abstract class DockContainer : DockModule
{ {
module.Owner = null; module.Owner = null;
module.Root = null; module.Root = null;
if (!_isCleaningUp)
{
CheckCleanup(); 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() protected virtual void CheckCleanup()
{ {
@@ -104,6 +138,10 @@ public abstract class DockContainer : DockModule
child.Root = null; child.Root = null;
} }
_children.Clear(); _children.Clear();
if (!_isCleaningUp)
{
CheckCleanup();
}
} }
protected override void OnRootChanged() protected override void OnRootChanged()

View File

@@ -57,10 +57,12 @@ public class DockPanel : DockContainer
if (owner != null) if (owner != null)
{ {
int index = owner.Children.IndexOf(this); owner.ReplaceChild(this, child);
owner.RemoveChild(this); }
child.Detach(); else if (Root != null && Root.RootPanel == this)
owner.InsertChild(index, child); {
// If this is the root panel, we can't easily replace it if the child is a DockGroup,
// because RootPanel must be a DockPanel. So we just leave it.
} }
} }
} }

View File

@@ -72,6 +72,8 @@ public class DockingLayout : Control
/// <param name="targetGroup">The target group to add the document to. If null, a suitable group will be found or created.</param> /// <param name="targetGroup">The target group to add the document to. If null, a suitable group will be found or created.</param>
public void AddDocument(DockDocument document, DockTarget target, DockGroup? targetGroup = null) public void AddDocument(DockDocument document, DockTarget target, DockGroup? targetGroup = null)
{ {
ArgumentNullException.ThrowIfNull(document);
if (targetGroup != null && targetGroup.Root != this) if (targetGroup != null && targetGroup.Root != this)
{ {
throw new ArgumentException("targetGroup does not belong to this DockingLayout"); throw new ArgumentException("targetGroup does not belong to this DockingLayout");