diff --git a/src/Editor/Ghost.Editor.Core/Controls/Internal/Docking/DockGroupNode.cs b/src/Editor/Ghost.Editor.Core/Controls/Internal/Docking/DockGroupNode.cs
index ef94e7f..e785b44 100644
--- a/src/Editor/Ghost.Editor.Core/Controls/Internal/Docking/DockGroupNode.cs
+++ b/src/Editor/Ghost.Editor.Core/Controls/Internal/Docking/DockGroupNode.cs
@@ -47,6 +47,10 @@ public partial class DockGroupNode : DockNode
///
/// The zero-based index at which node should be inserted.
/// The node to insert.
+ ///
+ /// If the node is already a child of this group, it will be moved to the specified index.
+ /// The index represents the desired final position in the collection.
+ ///
/// Thrown if node is null.
/// Thrown if index is less than 0 or greater than Children.Count.
/// Thrown if adding the node would create a cycle or if adding self.
@@ -68,15 +72,8 @@ public partial class DockGroupNode : DockNode
{
int oldIndex = _children.IndexOf(node);
if (oldIndex == index) return;
-
- // If we're moving an item forward, the target index will shift after removal
- int targetIndex = index;
- if (targetIndex > oldIndex) targetIndex--;
-
- if (oldIndex == targetIndex) return;
- _children.RemoveAt(oldIndex);
- _children.Insert(targetIndex, node);
+ _children.Move(oldIndex, index);
return;
}
diff --git a/src/Editor/Ghost.Editor/View/Controls/DockLayout.cs b/src/Editor/Ghost.Editor/View/Controls/DockLayout.cs
index 7f55ac5..6f794e7 100644
--- a/src/Editor/Ghost.Editor/View/Controls/DockLayout.cs
+++ b/src/Editor/Ghost.Editor/View/Controls/DockLayout.cs
@@ -430,86 +430,99 @@ public sealed partial class DockLayout : Control
return;
}
- // Remove from source first (if it's the same node, we'll re-add it to the new structure)
+ // If targetNode is the only child of Root, we wrap it.
+ if (Root.Children.Count == 1 && ReferenceEquals(Root.Children[0], targetNode))
+ {
+ // Remove from source first
+ if (!_sourceNode.Items.Remove(_draggedItem))
+ {
+ ClearDragOperationState();
+ return;
+ }
+
+ var newGroup = new DockGroupNode
+ {
+ Orientation = isHorizontalSplit ? Orientation.Horizontal : Orientation.Vertical
+ };
+
+ var newNode = new DockPanelNode();
+ newNode.Items.Add(_draggedItem);
+
+ Root.RemoveChild(targetNode);
+ Root.AddChild(newGroup);
+
+ if (isAfter)
+ {
+ newGroup.AddChild(targetNode);
+ newGroup.AddChild(newNode);
+ }
+ else
+ {
+ newGroup.AddChild(newNode);
+ newGroup.AddChild(targetNode);
+ }
+
+ CleanupEmptyNodes(_sourceNode);
+ }
+ else
+ {
+ ClearDragOperationState();
+ return;
+ }
+ }
+ else
+ {
+ int targetIndex = parentGroup.Children.IndexOf(targetNode);
+ if (targetIndex < 0)
+ {
+ ClearDragOperationState();
+ return;
+ }
+
+ // Remove from source
if (!_sourceNode.Items.Remove(_draggedItem))
{
ClearDragOperationState();
return;
}
- var newRoot = new DockGroupNode
+ if ((isHorizontalSplit && parentGroup.Orientation == Orientation.Horizontal) ||
+ (!isHorizontalSplit && parentGroup.Orientation == Orientation.Vertical))
{
- Orientation = isHorizontalSplit ? Orientation.Horizontal : Orientation.Vertical
- };
-
- var newNode = new DockPanelNode();
- newNode.Items.Add(_draggedItem);
-
- // If targetNode was the only child of Root, we replace it in Root.
- // If targetNode WAS the Root (impossible by type, but let's be safe with the model), we replace Root.
- // Actually, Root is always a DockGroupNode. So targetNode must be a child of Root if parent is null?
- // No, if parent is null it's either detached or it IS the root.
- // But targetNode is DockPanelNode, and Root is DockGroupNode.
-
- // If targetNode is detached, we can't split it.
- // Let's assume it's a child of a group that we don't have a reference to? No, Parent should be set.
- // The only case where Parent is null for a DockPanelNode in a valid tree is if it's NOT in the tree.
- // Wait, if Root has only one child, that child's parent SHOULD be Root.
-
- ClearDragOperationState();
- return; // Abort for now, parent should not be null in a valid tree for a Panel.
- }
-
- int targetIndex = parentGroup.Children.IndexOf(targetNode);
- if (targetIndex < 0)
- {
- ClearDragOperationState();
- return;
- }
-
- // Remove from source
- if (!_sourceNode.Items.Remove(_draggedItem))
- {
- ClearDragOperationState();
- return;
- }
-
- if ((isHorizontalSplit && parentGroup.Orientation == Orientation.Horizontal) ||
- (!isHorizontalSplit && parentGroup.Orientation == Orientation.Vertical))
- {
- // Same orientation, just insert next to it
- var newNode = new DockPanelNode();
- newNode.Items.Add(_draggedItem);
- parentGroup.InsertChild(isAfter ? targetIndex + 1 : targetIndex, newNode);
- }
- else
- {
- // Different orientation, need to replace targetNode with a new group
- parentGroup.RemoveChild(targetNode);
-
- var newGroup = new DockGroupNode
- {
- Orientation = isHorizontalSplit ? Orientation.Horizontal : Orientation.Vertical
- };
-
- var newNode = new DockPanelNode();
- newNode.Items.Add(_draggedItem);
-
- if (isAfter)
- {
- newGroup.AddChild(targetNode);
- newGroup.AddChild(newNode);
+ // Same orientation, just insert next to it
+ var newNode = new DockPanelNode();
+ newNode.Items.Add(_draggedItem);
+ parentGroup.InsertChild(isAfter ? targetIndex + 1 : targetIndex, newNode);
}
else
{
- newGroup.AddChild(newNode);
- newGroup.AddChild(targetNode);
+ // Different orientation, need to replace targetNode with a new group
+ parentGroup.RemoveChild(targetNode);
+
+ var newGroup = new DockGroupNode
+ {
+ Orientation = isHorizontalSplit ? Orientation.Horizontal : Orientation.Vertical
+ };
+
+ var newNode = new DockPanelNode();
+ newNode.Items.Add(_draggedItem);
+
+ if (isAfter)
+ {
+ newGroup.AddChild(targetNode);
+ newGroup.AddChild(newNode);
+ }
+ else
+ {
+ newGroup.AddChild(newNode);
+ newGroup.AddChild(targetNode);
+ }
+
+ parentGroup.InsertChild(targetIndex, newGroup);
}
- parentGroup.InsertChild(targetIndex, newGroup);
+ CleanupEmptyNodes(_sourceNode);
}
-
- CleanupEmptyNodes(_sourceNode);
}
ClearDragOperationState();
diff --git a/src/Test/Ghost.UnitTest/DockingModelTest.cs b/src/Test/Ghost.UnitTest/DockingModelTest.cs
index bc70ab8..1560a55 100644
--- a/src/Test/Ghost.UnitTest/DockingModelTest.cs
+++ b/src/Test/Ghost.UnitTest/DockingModelTest.cs
@@ -171,17 +171,23 @@ public class DockingModelTest
group.AddChild(child2);
group.AddChild(child3);
- // Move child1 to end
- group.InsertChild(3, child1);
+ // Move child1 to end (index 2)
+ group.InsertChild(2, child1);
Assert.AreEqual(child2, group.Children[0]);
Assert.AreEqual(child3, group.Children[1]);
Assert.AreEqual(child1, group.Children[2]);
- // Move child3 to start
+ // Move child3 to start (index 0)
group.InsertChild(0, child3);
Assert.AreEqual(child3, group.Children[0]);
Assert.AreEqual(child2, group.Children[1]);
Assert.AreEqual(child1, group.Children[2]);
+
+ // Move child2 forward by one (index 1 -> 2)
+ group.InsertChild(2, child2);
+ Assert.AreEqual(child3, group.Children[0]);
+ Assert.AreEqual(child1, group.Children[1]);
+ Assert.AreEqual(child2, group.Children[2]);
}
[TestMethod]
@@ -198,4 +204,20 @@ public class DockingModelTest
Assert.AreEqual(child1, group.Children[0]);
Assert.AreEqual(child2, group.Children[1]);
}
+
+ [TestMethod]
+ public void TestPanel_SetInvalidSelectedItem_ResetsSelection()
+ {
+ var panel = new DockPanelNode();
+ var item1 = new object();
+ var item2 = new object();
+
+ panel.Items.Add(item1);
+ panel.SelectedItem = item1;
+
+ panel.SelectedItem = item2; // Not in collection
+
+ Assert.IsNull(panel.SelectedItem);
+ Assert.AreEqual(-1, panel.SelectedIndex);
+ }
}