Closed Ron2 closed 8 years ago
Here's the implementation of IOrderedInsertionContext that works in one version of LevelEditor:
#region IOrderedInsertionContext Members
/// <summary>
/// Returns true if 'item' can be inserted.</summary>
/// <param name="parent">The object that will become the parent of the inserted object.
/// GameContext requires this to not be null.</param>
/// <param name="before">The object that is immediately before the inserted object.
/// Can be null to indicate that the inserted item should become the first child.</param>
/// <param name="item">The item to be inserted</param>
/// <returns>True iff 'item' can be successfully inserted</returns>
bool IOrderedInsertionContext.CanInsert(object parent, object before, object itemDataObject)
{
// We can't let the root be replaced, so we will require 'parent' to be valid.
var parentNode = parent.As<DomNode>();
if (parentNode == null)
return false;
var beforeNode = before.As<DomNode>();
// Convert from the DragEventArgs data to DomNodes.
IEnumerable<object> items = Util.ConvertData(itemDataObject, false);
var insertNodes = new List<DomNode>();
foreach (object item in items)
{
DomNode insertNode = item as DomNode;
if (insertNode != null)
insertNodes.Add(insertNode);
}
if (insertNodes.Count == 0)
return false;
// If the user has selected multiple nodes and some of those nodes are children of other
// selected nodes, remove the children from our list. This preserves the hierarchy. This
// behavior is like the Mac OS X Finder, for example.
insertNodes = new List<DomNode>(DomNode.GetRoots(insertNodes));
// Find a compatible ChildInfo (from the parent) for each inserted DomNode.
// The order of the ChildInfos determines the order that the Project Lister displays items.
// This code assumes that TreeControl gives us the nodes to insert in the same order that
// they're being displayed (and not in the order that the user happened to do the multi-
// select.)
// So, we can step through the ChildInfos and 'insertNodes', from beginning to end.
var childInfos = new List<ChildInfo>(insertNodes.Count);
{
bool foundFirst = false; // Find the first ChildInfo that matches 'beforeNode'.
int i = 0;
foreach (ChildInfo info in parentNode.Type.Children)
{
if (!foundFirst)
{
if (beforeNode == null)
foundFirst = true;
else if (info.Type.IsAssignableFrom(beforeNode.Type))
foundFirst = true;
else
continue;
}
while (info.Type.IsAssignableFrom(insertNodes[i].Type))
{
childInfos.Add(info);
i++;
if (i == insertNodes.Count)
break;
}
if (i == insertNodes.Count)
break;
}
if (childInfos.Count != insertNodes.Count)
return false;
}
// Do a sanity check on the DomNode hierarchy.
for(int i = 0; i < insertNodes.Count; i++)
{
DomNode insertNode = insertNodes[i];
ChildInfo childInfo = childInfos[i];
// We can only currently insert into a list.
if (!childInfo.IsList)
return false;
// Avoid useless inserts.
if (parentNode == insertNode || beforeNode == insertNode)
return false;
// We can't insert a node into itself or a descendant of itself.
if (parentNode.IsDescendantOf(insertNode))
return false;
// Make sure that itemNode is compatible with the parent's ChildInfo.
if (!childInfo.Type.IsAssignableFrom(insertNode.Type))
return false;
}
return true;
}
/// <summary>
/// Inserts 'item' into the set of objects at the desired position. Will only be called
/// if CanInsert() returns true.</summary>
/// <param name="parent">The object that will become the parent of the inserted object.</param>
/// <param name="before">The object that is immediately before the inserted object.
/// Can be null to indicate that the inserted item should become the first child.</param>
/// <param name="item">The item to be inserted.</param>
void IOrderedInsertionContext.Insert(object parent, object before, object itemDataObject)
{
var parentNode = parent.Cast<DomNode>();
var beforeNode = before.As<DomNode>();
// Convert to DomNodes.
IEnumerable<object> items = Util.ConvertData(itemDataObject, false);
var insertNodes = new List<DomNode>();
foreach (object item in items)
{
DomNode insertNode = item as DomNode;
if (insertNode != null)
insertNodes.Add(insertNode);
}
// If the user has selected multiple nodes and some of those nodes are children of other
// selected nodes, remove the children from our list. This preserves the hierarchy. This
// behavior is like the Mac OS X Finder, for example.
insertNodes = new List<DomNode>(DomNode.GetRoots(insertNodes));
// Remove the DomNodes from their parents before calculating the insertion index.
foreach (DomNode insertNode in insertNodes)
insertNode.RemoveFromParent();
// Find a compatible ChildInfo (from the parent) for each inserted DomNode.
// The order of the ChildInfos determines the order that the Project Lister displays items,
// so we need to step through the ChildInfos and match inserted DomNodes at the same time.
var childInfos = new List<ChildInfo>(insertNodes.Count);
{
bool foundFirst = false;
int i = 0;
foreach (ChildInfo info in parentNode.Type.Children)
{
if (!foundFirst)
{
if (beforeNode == null)
foundFirst = true;
else if (info.Type.IsAssignableFrom(beforeNode.Type))
foundFirst = true;
else
continue;
}
while (info.Type.IsAssignableFrom(insertNodes[i].Type))
{
childInfos.Add(info);
i++;
if (i == insertNodes.Count)
break;
}
if (i == insertNodes.Count)
break;
}
}
// Insert the previously removed DomNodes, using the correct ChildInfo.
int insertionIndex;
if (beforeNode == null)
insertionIndex = 0;
else
{
IList<DomNode> childList = parentNode.GetChildList(childInfos[0]);
insertionIndex = childList.IndexOf(beforeNode) + 1;
}
ChildInfo priorInfo = childInfos[0];
for (int i = 0; i < insertNodes.Count; i++)
{
DomNode insertNode = insertNodes[i];
ChildInfo info = childInfos[i];
if (info != priorInfo)
{
priorInfo = info;
insertionIndex = 0;
}
IList<DomNode> childList = parentNode.GetChildList(info);
childList.Insert(insertionIndex, insertNode);
insertionIndex++;
}
}
#endregion
Thank you Ron, I merged all the changes to trunk. Alan
If a TreeControl’s ShowDragBetweenCue is set to true, and if the context can be adapted to the new interface, IOrderedInsertionContext, then the user will be allowed to re-order nodes by dragging. Also, objects can be dragged into a specific location within a tree, like when dragging from the palette.