/// <summary> /// Validates this canvas, checking for any broken nodes or references and cleans them. /// </summary> public void Validate() { NodeEditor.checkInit(false); // Check Groups CheckNodeCanvasList(ref groups, "groups"); // Check Nodes and their connection ports CheckNodeCanvasList(ref nodes, "nodes"); foreach (Node node in nodes) { ConnectionPortManager.UpdateConnectionPorts(node); node.canvas = this; foreach (ConnectionPort port in node.connectionPorts) { port.Validate(node); } } // Check EditorStates if (editorStates == null) { editorStates = new NodeEditorState[0]; } editorStates = editorStates.Where((NodeEditorState state) => state != null).ToArray(); foreach (NodeEditorState state in editorStates) { bool isValidate = true; foreach (var node in state.selectedNodes) { if (!nodes.Contains(node)) { isValidate = false; } } if (!isValidate) { state.selectedNodes.Clear(); } } // Validate CanvasType-specific stuff ValidateSelf(); }
private static void HandleWindowPanning(NodeEditorInputInfo inputInfo) { NodeEditorState state = inputInfo.editorState; if (state.panWindow) { // Calculate change in panOffset if (inputInfo.editorState.dragUserID == "window") { state.panOffset += state.UpdateDrag("window", inputInfo.inputPos); } else { state.panWindow = false; } NodeEditor.RepaintClients(); } }
/// <summary> /// Creates a canvas of the specified generic type /// </summary> public static T CreateCanvas <T>(bool edit = false) where T : NodeCanvas { if (typeof(T) == typeof(NodeCanvas)) { throw new Exception("Cannot create canvas of type 'NodeCanvas' as that is only the base class. Please specify a valid subclass!"); } T canvas = ScriptableObject.CreateInstance <T>(); canvas.name = canvas.saveName = "New " + canvas.canvasName; NodeEditor.BeginEditingCanvas(canvas); canvas.OnCreate(); if (!edit) { NodeEditor.EndEditingCanvas(); } return(canvas); }
/// <summary> /// Loads the mainNodeCanvas and it's associated mainEditorState from an asset at path /// </summary> public void LoadNodeCanvas(string path) { // Try to load the NodeCanvas if (!File.Exists(path) || (nodeCanvas = NodeEditorSaveManager.LoadNodeCanvas(path, true)) == null) { NewNodeCanvas(); return; } editorState = NodeEditorSaveManager.ExtractEditorState(nodeCanvas, MainEditorStateIdentifier); openedCanvasPath = path; if (useCache) { SaveCache(); } NodeEditor.RecalculateAll(nodeCanvas); NodeEditor.RepaintClients(); }
private static void HandleNodeDragging(NodeEditorInputInfo inputInfo) { NodeEditorState state = inputInfo.editorState; if (state.dragNode) { // If conditions apply, drag the selected node, else disable dragging if (state.selectedNode != null && inputInfo.editorState.dragUserID == "node") { // Apply new position for the dragged node state.UpdateDrag("node", inputInfo.inputPos); state.selectedNode.position = state.dragObjectPos; NodeEditor.RepaintClients(); } else { state.dragNode = false; } } }
[EventHandlerAttribute(EventType.MouseDown, -2)] // Absolute second to call! private static void HandleSelecting(NodeEditorInputInfo inputInfo) { NodeEditorState state = inputInfo.editorState; if (inputInfo.inputEvent.button == 0 && state.focusedNode != state.selectedNode) { // Select focussed Node unfocusControlsForState = state; state.selectedNode = state.focusedNode; Debug.Log("Set Selected node " + state.selectedNode); NodeEditor.RepaintClients(); #if UNITY_EDITOR if (state.selectedNode != null) { UnityEditor.Selection.activeObject = state.selectedNode; } #endif } }
private static void HandleNodeSnap(NodeEditorInputInfo inputInfo) { NodeEditorState state = inputInfo.editorState; if (state.selectedNode != null) { // Snap selected Node's position and the drag to multiples of 10 state.selectedNode.rect.x = Mathf.Round(state.selectedNode.rect.x / 10) * 10; state.selectedNode.rect.y = Mathf.Round(state.selectedNode.rect.y / 10) * 10; inputInfo.inputEvent.Use(); } if (state.activeGroup != null) { state.activeGroup.rect.x = Mathf.Round(state.activeGroup.rect.x / 10) * 10; state.activeGroup.rect.y = Mathf.Round(state.activeGroup.rect.y / 10) * 10; inputInfo.inputEvent.Use(); } NodeEditor.RepaintClients(); }
/// <summary> /// Loads the canvas from the cache save file /// Called whenever a reload was made /// </summary> public void LoadCache() { #if CACHE if (!useCache) { // Simply create a ne canvas NewNodeCanvas(); return; } bool skipLoad = false; if (cacheMemorySODump) { // Check if a memory dump has been found, if so, load that nodeCanvas = ResourceManager.LoadResource <NodeCanvas>(SOMemoryDumpPath); if (nodeCanvas != null && !nodeCanvas.Validate(false)) { Debug.LogWarning("Cache Dump corrupted! Loading crash-proof lastSession, you might have lost a bit of work. \n " + "To prevent this from happening in the future, allow the Node Editor to properly save the cache " + "by clicking out of the window before switching scenes, since there are no callbacks to facilitate this!"); nodeCanvas = null; UnityEditor.AssetDatabase.DeleteAsset(SOMemoryDumpPath); } if (nodeCanvas != null) { skipLoad = true; } } // Try to load the NodeCanvas if (!skipLoad && (!File.Exists(lastSessionPath) || (nodeCanvas = NodeEditorSaveManager.LoadNodeCanvas(lastSessionPath, cacheWorkingCopy)) == null) && // Check for asset cache (nodeCanvas = NodeEditorSaveManager.LoadSceneNodeCanvas("lastSession", cacheWorkingCopy)) == null) // Check for scene cache { NewNodeCanvas(); return; } // Fetch the associated MainEditorState editorState = NodeEditorSaveManager.ExtractEditorState(nodeCanvas, MainEditorStateIdentifier); UpdateCanvasInfo(); nodeCanvas.Validate(); nodeCanvas.TraverseAll(); NodeEditor.RepaintClients(); #endif }
/// <summary> /// Applies a connection between output and input. 'CanApplyConnection' has to be checked before /// </summary> public static void ApplyConnection(NodeOutput output, NodeInput input) { if (input != null && output != null) { if (input.connection != null) { input.connection.connections.Remove(input); } input.connection = output; output.connections.Add(input); if (input.body.shouldCalculate) { NodeEditor.RecalculateFrom(input.body); } NodeEditorCallbacks.IssueOnAddConnection(input); } }
private static void HandleNodeDragging(NodeEditorInputInfo inputInfo) { NodeEditorState editorState = inputInfo.editorState; if (editorState.dragNode) { if ((UnityEngine.Object)editorState.selectedNode != (UnityEngine.Object)null && GUIUtility.hotControl == 0) { editorState.dragOffset = inputInfo.inputPos - editorState.dragStart; editorState.selectedNode.rect.position = editorState.dragPos + editorState.dragOffset * editorState.zoom; NodeEditorCallbacks.IssueOnMoveNode(editorState.selectedNode); NodeEditor.RepaintClients(); } else { editorState.dragNode = false; } } }
public void OnGUI() { // Initiation NodeEditor.checkInit(); if (NodeEditor.InitiationError) { GUILayout.Label("Initiation failed! Check console for more information!"); return; } AssureHasEditor(); if (mainNodeCanvas == null) { NewNodeCanvas(); } //Debug.Log ("mainNodeCanvas " + mainNodeCanvas.name); // Specify the Canvas rect in the EditorState mainEditorState.canvasRect = canvasWindowRect; // If you want to use GetRect: // Rect canvasRect = GUILayoutUtility.GetRect (600, 600); // if (Event.current.type != EventType.Layout) // mainEditorState.canvasRect = canvasRect; // Perform drawing with error-handling try { NodeEditor.DrawCanvas(mainNodeCanvas, mainEditorState); } catch (UnityException e) { // on exceptions in drawing flush the canvas to avoid locking the ui. NewNodeCanvas(); Debug.LogError("Unloaded Canvas due to exception in Draw!"); Debug.LogException(e); } // Draw Side Window sideWindowWidth = Math.Min(600, Math.Max(200, (int)(position.width / 5))); NodeEditorGUI.StartNodeGUI(); GUILayout.BeginArea(sideWindowRect, GUI.skin.box); DrawSideWindow(); GUILayout.EndArea(); NodeEditorGUI.EndNodeGUI(); }
/// <summary> /// Resizes the node to either the MinSize or to fit size of the GUILayout in #NodeGUI() /// </summary> protected internal virtual void ResizeNode() { if (Event.current.type != EventType.Repaint) { return; } if (!Resizable) { return; } Rect nodeRect = rect; Vector2 maxSize = new Vector2(); maxSize.y = Math.Max(resizeToPosition.y, MinSize.y); List <NodeKnob> topBottomKnobs = nodeKnobs.Where(x => x.side == NodeSide.Bottom || x.side == NodeSide.Top).ToList(); if (topBottomKnobs.Any()) { float knobSize = topBottomKnobs.Max(x => x.GetGUIKnob().xMax - nodeRect.xMin); float minWidth = MinSize.x; maxSize.x = Math.Max(knobSize, minWidth); } else { maxSize.x = MinSize.x; } if (maxSize != nodeRect.size) { nodeRect.size = maxSize; } if (rect.size != nodeRect.size) { rect = nodeRect; NodeEditor.RepaintClients(); } }
/// <summary> /// Creates a canvas of the specified canvasType as long as it is a subclass of NodeCanvas /// </summary> public static NodeCanvas CreateCanvas(Type canvasType) { NodeCanvas canvas; if (canvasType != null && canvasType.IsSubclassOf(typeof(NodeCanvas))) { canvas = ScriptableObject.CreateInstance(canvasType) as NodeCanvas; } else { return(null); } canvas.name = canvas.saveName = "New " + canvas.canvasName; NodeEditor.BeginEditingCanvas(canvas); canvas.OnCreate(); NodeEditor.EndEditingCanvas(); return(canvas); }
/// <summary> /// Draws the node curves; splitted from knobs because of the render order /// </summary> public void DrawConnections() { for (int outCnt = 0; outCnt < Outputs.Count; outCnt++) { NodeOutput output = Outputs [outCnt]; Vector2 startPos = output.GetGUIKnob().center; Vector2 startDir = output.GetDirection(); for (int conCnt = 0; conCnt < output.connections.Count; conCnt++) { NodeInput input = output.connections [conCnt]; NodeEditor.DrawConnection(startPos, startDir, input.GetGUIKnob().center, input.GetDirection() * -1, ConnectionTypes.GetTypeData(output.type).col); } } }
private static void HandleNodeSnap(NodeEditorInputInfo inputInfo) { if (inputInfo.inputEvent.modifiers == EventModifiers.Control || inputInfo.inputEvent.keyCode == KeyCode.LeftControl) { NodeEditorState state = inputInfo.editorState; if (state.selectedNode != null) { // Snap selected Node's position to multiples of 10 state.selectedNode.position.x = Mathf.Round(state.selectedNode.rect.x / 10) * 10; state.selectedNode.position.y = Mathf.Round(state.selectedNode.rect.y / 10) * 10; NodeEditor.RepaintClients(); } if (state.activeGroup != null) { // Snap active Group's position to multiples of 10 state.activeGroup.rect.x = Mathf.Round(state.activeGroup.rect.x / 10) * 10; state.activeGroup.rect.y = Mathf.Round(state.activeGroup.rect.y / 10) * 10; NodeEditor.RepaintClients(); } } }
private static void HandleNodeDragging(NodeEditorInputInfo inputInfo) { NodeEditorState state = inputInfo.editorState; if (state.dragNode) { // If conditions apply, drag the selected node, else disable dragging if (state.selectedNode != null && GUIUtility.hotControl == 0) { // Calculate new position for the dragged object state.dragOffset = inputInfo.inputPos - state.dragStart; state.selectedNode.rect.position = state.dragPos + state.dragOffset * state.zoom; NodeEditorCallbacks.IssueOnMoveNode(state.selectedNode); NodeEditor.RepaintClients(); } else { state.dragNode = false; } } }
private static void KB_MoveNode(NodeEditorInputInfo inputInfo) { NodeEditorState state = inputInfo.editorState; if (state.selectedNode != null) { Vector2 pos = state.selectedNode.rect.position; int shiftAmount = 0; // Increase the distance moved to 10 if shift is being held. if (inputInfo.inputEvent.shift) { shiftAmount = 10; } else { shiftAmount = 5; } if (inputInfo.inputEvent.keyCode == KeyCode.RightArrow) { pos = new Vector2(pos.x + shiftAmount, pos.y); } else if (inputInfo.inputEvent.keyCode == KeyCode.LeftArrow) { pos = new Vector2(pos.x - shiftAmount, pos.y); } else if (inputInfo.inputEvent.keyCode == KeyCode.DownArrow) { pos = new Vector2(pos.x, pos.y + shiftAmount); } else if (inputInfo.inputEvent.keyCode == KeyCode.UpArrow) { pos = new Vector2(pos.x, pos.y - shiftAmount); } state.selectedNode.rect.position = pos; inputInfo.inputEvent.Use(); } NodeEditor.RepaintClients(); }
public void DrawSideWindow() { GUILayout.Label(new GUIContent("Node Editor (" + mainNodeCanvas.name + ")", "Opened Canvas path: " + openedCanvasPath), NodeEditorGUI.nodeLabelBold); if (GUILayout.Button(new GUIContent("Save Canvas", "Saves the Canvas to a Canvas Save File in the Assets Folder"))) { SaveNodeCanvas(EditorUtility.SaveFilePanelInProject("Save Node Canvas", "Node Canvas", "asset", "", ResourceManager.resourcePath + "Saves/")); } if (GUILayout.Button(new GUIContent("Load Canvas", "Loads the Canvas from a Canvas Save File in the Assets Folder"))) { string path = EditorUtility.OpenFilePanel("Load Node Canvas", ResourceManager.resourcePath + "Saves/", "asset"); if (!path.Contains(Application.dataPath)) { if (path != String.Empty) { ShowNotification(new GUIContent("You should select an asset inside your project folder!")); } return; } path = path.Replace(Application.dataPath, "Assets"); LoadNodeCanvas(path); } if (GUILayout.Button(new GUIContent("New Canvas", "Loads an empty Canvas"))) { NewNodeCanvas(); } if (GUILayout.Button(new GUIContent("Recalculate All", "Initiates complete recalculate. Usually does not need to be triggered manually."))) { NodeEditor.RecalculateAll(mainNodeCanvas); } if (GUILayout.Button("Force Re-Init")) { NodeEditor.ReInit(true); } NodeEditorGUI.knobSize = EditorGUILayout.IntSlider(new GUIContent("Handle Size", "The size of the Node Input/Output handles"), NodeEditorGUI.knobSize, 12, 20); mainEditorState.zoom = EditorGUILayout.Slider(new GUIContent("Zoom", "Use the Mousewheel. Seriously."), mainEditorState.zoom, 0.6f, 2); }
/// <summary> /// Loads the mainNodeCanvas and it's associated mainEditorState from an asset at path /// </summary> public void LoadSceneNodeCanvas(string path) { #if UNITY_EDITOR if (useCache) { DeleteCache(); } #endif // Try to load the NodeCanvas if ((nodeCanvas = NodeEditorSaveManager.LoadSceneNodeCanvas(path, true)) == null) { NewNodeCanvas(); return; } editorState = NodeEditorSaveManager.ExtractEditorState(nodeCanvas, MainEditorStateIdentifier); openedCanvasPath = path; NodeEditor.RecalculateAll(nodeCanvas); NodeEditor.RepaintClients(); }
/// <summary> /// Gets the type data for the specified type name, if declared /// </summary> public static TypeData GetTypeData(string typeName) { if (types == null || types.Count == 0) { NodeEditor.ReInit(false); } TypeData typeData; if (!types.TryGetValue(typeName, out typeData)) { Debug.LogError("No TypeData defined for: " + typeName); typeData = types.First().Value; } if (typeData.declaration == null || typeData.InputKnob == null || typeData.OutputKnob == null) { NodeEditor.ReInit(false); typeData = GetTypeData(typeName); } return(typeData); }
/// <summary> /// Applies a connection between the passed NodeOutput and this NodeInput. 'CanApplyConnection' has to be checked before to avoid interferences! /// </summary> public void ApplyConnection(NodeOutput output) { if (output == null) { return; } if (connection != null) { NodeEditorCallbacks.IssueOnRemoveConnection(this); connection.connections.Remove(this); } connection = output; output.connections.Add(this); NodeEditor.RecalculateFrom(body); output.body.OnAddOutputConnection(output); body.OnAddInputConnection(this); NodeEditorCallbacks.IssueOnAddConnection(this); }
[EventHandlerAttribute(-4)] // Absolute first to call! private static void HandleFocussing(NodeEditorInputInfo inputInfo) { NodeEditorState state = inputInfo.editorState; // Choose focused Node state.focusedNode = NodeEditor.NodeAtPosition(NodeEditor.ScreenToCanvasSpace(inputInfo.inputPos), out state.focusedConnectionKnob); // Perform focus changes in Repaint, which is the only suitable time to do this if (unfocusControlsForState == state && Event.current.type == EventType.Repaint) { if (unfocusControlsHot == GUIUtility.hotControl) { GUIUtility.hotControl = 0; } if (unfocusControlsKeyboard == GUIUtility.keyboardControl) { GUIUtility.keyboardControl = 0; } unfocusControlsForState = null; } }
private static void HandleNodeDragging(NodeEditorInputInfo inputInfo) { NodeEditorState state = inputInfo.editorState; if (state.dragNode) { // If conditions apply, drag the selected node, else disable dragging if (state.selectedNode != null && GUIUtility.hotControl == 0) { // Calculate new position for the dragged object state.dragOffset = inputInfo.inputPos - state.dragStart; Vector2 delta = (state.dragPos + state.dragOffset * state.zoom) - state.selectedNode.rect.position; if (delta.magnitude > 1.0f) { state.didDragNode = true; } if (state.selectedNode != null && !state.selectedNodes.Contains(state.selectedNode)) { state.selectedNode.rect.position = state.dragPos + state.dragOffset * state.zoom; } Vector2 deltaAll = (state.dragOffset * state.zoom); foreach (var n in state.selectedNodes) { n.rect.position += delta; } if (inputInfo.inputEvent.alt) { Dictionary <Node, int> d = new Dictionary <Node, int>(); d[state.selectedNode] = 1; MoveChildren(ref d, state.selectedNode, delta); } NodeEditorCallbacks.IssueOnMoveNode(state.selectedNode); NodeEditor.RepaintClients(); } else { state.dragNode = false; } } }
/// <summary> /// Loads the mainNodeCanvas and it's associated mainEditorState from an asset at path /// </summary> public void LoadNodeCanvas(string path) { // Try to load the NodeCanvas if (!File.Exists(path) || (nodeCanvas = NodeEditorSaveManager.LoadNodeCanvas(path, true)) == null) { NewNodeCanvas(); return; } editorState = NodeEditorSaveManager.ExtractEditorState(nodeCanvas, MainEditorStateIdentifier); openedCanvasPath = path; nodeCanvas.Validate(); RecreateCache(); UpdateCanvasInfo(); nodeCanvas.TraverseAll(); NodeEditor.RepaintClients(); #if UNITY_EDITOR UnityEditor.AssetDatabase.DeleteAsset(SOMemoryDumpPath); NodeEditorUndoActions.CompleteSOMemoryDump(nodeCanvas); #endif }
private static void KB_MoveNode(NodeEditorInputInfo inputInfo) { if (GUIUtility.keyboardControl > 0) { return; } NodeEditorState state = inputInfo.editorState; if (state.selectedNode != null) { Vector2 pos = state.selectedNode.rect.position; int shiftAmount = inputInfo.inputEvent.shift? 50 : 10; if (inputInfo.inputEvent.keyCode == KeyCode.RightArrow || inputInfo.inputEvent.keyCode == KeyCode.D) { pos = new Vector2(pos.x + shiftAmount, pos.y); } else if (inputInfo.inputEvent.keyCode == KeyCode.LeftArrow || inputInfo.inputEvent.keyCode == KeyCode.A) { pos = new Vector2(pos.x - shiftAmount, pos.y); } else if (inputInfo.inputEvent.keyCode == KeyCode.DownArrow || inputInfo.inputEvent.keyCode == KeyCode.S) { pos = new Vector2(pos.x, pos.y + shiftAmount); } else if (inputInfo.inputEvent.keyCode == KeyCode.UpArrow || inputInfo.inputEvent.keyCode == KeyCode.W) { pos = new Vector2(pos.x, pos.y - shiftAmount); } state.selectedNode.position = pos; inputInfo.inputEvent.Use(); } NodeEditor.RepaintClients(); }
/// <summary> /// Loads the mainNodeCanvas and it's associated mainEditorState from an asset at path /// </summary> public void LoadSceneNodeCanvas(string path) { if (path.StartsWith("SCENE/")) { path = path.Substring(6); } // Try to load the NodeCanvas if ((nodeCanvas = NodeEditorSaveManager.LoadSceneNodeCanvas(path, true)) == null) { NewNodeCanvas(); return; } editorState = NodeEditorSaveManager.ExtractEditorState(nodeCanvas, MainEditorStateIdentifier); openedCanvasPath = path; nodeCanvas.Validate(); RecreateCache(); UpdateCanvasInfo(); nodeCanvas.TraverseAll(); NodeEditor.RepaintClients(); }
[EventHandlerAttribute(EventType.MouseDown, -1)] // Before the other context clicks because they won't account for groups private static void HandleGroupContextClick(NodeEditorInputInfo inputInfo) { NodeEditorState state = inputInfo.editorState; if (inputInfo.inputEvent.button == 1 && state.focusedNode == null) { // Right-click NOT on a node NodeGroup focusedGroup = HeaderAtPosition(state, NodeEditor.ScreenToCanvasSpace(inputInfo.inputPos)); if (focusedGroup != null) { // Context click for the group. This is static, not dynamic, because it would be useless GenericMenu context = new GenericMenu(); context.AddItem(new GUIContent("Delete"), false, () => { NodeEditor.curNodeCanvas = state.canvas; focusedGroup.Delete(); }); context.ShowAsContext(); inputInfo.inputEvent.Use(); } } }
/// <summary> /// Gets the type data for the specified type or, if not declared and checked, creates a new one for that type /// </summary> public static TypeData GetTypeData(Type type, bool createIfNotDeclared) { if (types == null || types.Count == 0) { NodeEditor.ReInit(false); } TypeData typeData = types.Values.First((TypeData tData) => tData.Type == type); if (typeData == null) { if (createIfNotDeclared) { typeData = new TypeData(type); types.Add(type.FullName, typeData); } else { typeData = types.First().Value; Debug.LogError("No TypeData defined for: " + type.FullName + "!"); } } return(typeData); }
/// <summary> /// Resizes the node to either the MinSize or to fit size of the GUILayout in NodeGUI /// </summary> protected internal virtual void AutoLayoutNode() { if (!AutoLayout || Event.current.type != EventType.Repaint) { return; } Rect nodeRect = rect; Vector2 size = new Vector2(); size.y = Math.Max(nodeGUIHeight.y, MinSize.y) + 4; // TODO: Figure out this thing // Account for potential knobs that might occupy horizontal space //float knobSize = 0; //List<ConnectionKnob> verticalKnobs = connectionKnobs.Where (x => x.side == NodeSide.Bottom || x.side == NodeSide.Top).ToList (); //if (verticalKnobs.Count > 0) // knobSize = verticalKnobs.Max ((ConnectionKnob knob) => knob.GetGUIKnob ().xMax - nodeRect.xMin); size.x = Math.Max(0, MinSize.x); autoSize = size; NodeEditor.RepaintClients(); }
/// <summary> /// Creates a canvas of the specified canvasType as long as it is a subclass of NodeCanvas /// </summary> public static NodeCanvas CreateCanvas(Type canvasType) { NodeCanvas canvas; if (canvasType != null && canvasType.IsSubclassOf(typeof(NodeCanvas))) { canvas = ScriptableObject.CreateInstance(canvasType) as NodeCanvas; } else { // TODO: Why can I not access StartMissionNode here??????? switch (NodeEditorGUI.state) { case NodeEditorGUI.NodeEditorState.Mission: canvas = ScriptableObject.CreateInstance <QuestCanvas>(); break; case NodeEditorGUI.NodeEditorState.Dialogue: canvas = ScriptableObject.CreateInstance <NodeEditorFramework.Standard.DialogueCanvas>(); break; case NodeEditorGUI.NodeEditorState.Sector: canvas = ScriptableObject.CreateInstance <NodeEditorFramework.Standard.SectorCanvas> (); break; default: canvas = ScriptableObject.CreateInstance <QuestCanvas>(); break; } } canvas.name = canvas.saveName = "New " + canvas.canvasName; NodeEditor.BeginEditingCanvas(canvas); canvas.OnCreate(); NodeEditor.EndEditingCanvas(); return(canvas); }