using UnityEditor.Experimental.GraphView; using UnityEngine; using UnityEditor; using UnityEditor.UIElements; using UnityEngine.UIElements; using System.Collections.Generic; using System.Linq; namespace GraphProcessor { /// /// Stack node view implementation, can be used to stack multiple node inside a context like VFX graph does. /// public class BaseStackNodeView : StackNode { public delegate void ReorderNodeAction(BaseNodeView nodeView, int oldIndex, int newIndex); /// /// StackNode data from the graph /// protected internal BaseStackNode stackNode; protected BaseGraphView owner; readonly string styleSheet = "GraphProcessorStyles/BaseStackNodeView"; /// Triggered when a node is re-ordered in the stack. public event ReorderNodeAction onNodeReordered; public BaseStackNodeView(BaseStackNode stackNode) { this.stackNode = stackNode; styleSheets.Add(Resources.Load(styleSheet)); } /// protected override void OnSeparatorContextualMenuEvent(ContextualMenuPopulateEvent evt, int separatorIndex) { // TODO: write the context menu for stack node } /// /// Called after the StackNode have been added to the graph view /// public virtual void Initialize(BaseGraphView graphView) { owner = graphView; headerContainer.Add(new Label(stackNode.title)); SetPosition(new Rect(stackNode.position, Vector2.one)); InitializeInnerNodes(); } void InitializeInnerNodes() { int i = 0; // Sanitize the GUID list in case some nodes were removed stackNode.nodeGUIDs.RemoveAll(nodeGUID => { if (owner.graph.nodesPerGUID.ContainsKey(nodeGUID)) { var node = owner.graph.nodesPerGUID[nodeGUID]; var view = owner.nodeViewsPerNode[node]; view.AddToClassList("stack-child__" + i); i++; AddElement(view); return false; } else { return true; // remove the entry as the GUID doesn't exist anymore } }); } /// public override void SetPosition(Rect newPos) { base.SetPosition(newPos); stackNode.position = newPos.position; } /// protected override bool AcceptsElement(GraphElement element, ref int proposedIndex, int maxIndex) { bool accept = base.AcceptsElement(element, ref proposedIndex, maxIndex); if (accept && element is BaseNodeView nodeView) { var index = Mathf.Clamp(proposedIndex, 0, stackNode.nodeGUIDs.Count - 1); int oldIndex = stackNode.nodeGUIDs.FindIndex(g => g == nodeView.nodeTarget.GUID); if (oldIndex != -1) { stackNode.nodeGUIDs.Remove(nodeView.nodeTarget.GUID); if (oldIndex != index) onNodeReordered?.Invoke(nodeView, oldIndex, index); } stackNode.nodeGUIDs.Insert(index, nodeView.nodeTarget.GUID); } return accept; } public override bool DragLeave(DragLeaveEvent evt, IEnumerable selection, IDropTarget leftTarget, ISelection dragSource) { foreach (var elem in selection) { if (elem is BaseNodeView nodeView) stackNode.nodeGUIDs.Remove(nodeView.nodeTarget.GUID); } return base.DragLeave(evt, selection, leftTarget, dragSource); } } }