using System; using System.Collections.Generic; using System.Linq; using UnityEditor; using UnityEngine; namespace XNodeEditor { /// Base class to derive custom Node Graph editors from. Use this to override how graphs are drawn in the editor. [CustomNodeGraphEditor(typeof(XNode.NodeGraph))] public class NodeGraphEditor : XNodeEditor.Internal.NodeEditorBase { /// The position of the window in screen space. public Rect position; /// Are we currently renaming a node? protected bool isRenaming; public virtual void OnGUI() { } public virtual Texture2D GetGridTexture() { return NodeEditorPreferences.GetSettings().gridTexture; } public virtual Texture2D GetSecondaryGridTexture() { return NodeEditorPreferences.GetSettings().crossTexture; } /// Return default settings for this graph type. This is the settings the user will load if no previous settings have been saved. public virtual NodeEditorPreferences.Settings GetDefaultPreferences() { return new NodeEditorPreferences.Settings(); } /// Returns context node menu path. Null or empty strings for hidden nodes. public virtual string GetNodeMenuName(Type type) { //Check if type has the CreateNodeMenuAttribute XNode.Node.CreateNodeMenuAttribute attrib; if (NodeEditorUtilities.GetAttrib(type, out attrib)) // Return custom path return attrib.menuName; else // Return generated path return ObjectNames.NicifyVariableName(type.ToString().Replace('.', '/')); } /// Add items for the context menu when right-clicking this node. Override to add custom menu items. public virtual void AddContextMenuItems(GenericMenu menu) { Vector2 pos = NodeEditorWindow.current.WindowToGridPosition(Event.current.mousePosition); for (int i = 0; i < NodeEditorWindow.nodeTypes.Length; i++) { Type type = NodeEditorWindow.nodeTypes[i]; //Get node context menu path string path = GetNodeMenuName(type); if (string.IsNullOrEmpty(path)) continue; menu.AddItem(new GUIContent(path), false, () => { CreateNode(type, pos); }); } menu.AddSeparator(""); menu.AddItem(new GUIContent("Add comment"), false, () => CreateComment(pos)); menu.AddSeparator(""); menu.AddItem(new GUIContent("Preferences"), false, () => NodeEditorWindow.OpenPreferences()); NodeEditorWindow.AddCustomContextMenuItems(menu, target); } /// Add items for the context menu when right-clicking this node. Override to add custom menu items. public virtual void AddCommentContextMenuItems(GenericMenu menu) { Vector2 pos = NodeEditorWindow.current.WindowToGridPosition(Event.current.mousePosition); for (int i = 0; i < NodeEditorWindow.nodeTypes.Length; i++) { Type type = NodeEditorWindow.nodeTypes[i]; //Get node context menu path string path = GetNodeMenuName(type); if (string.IsNullOrEmpty(path)) continue; menu.AddItem(new GUIContent(path), false, () => { CreateNode(type, pos); }); } menu.AddSeparator(""); menu.AddItem(new GUIContent("Add comment"), false, () => CreateComment(pos)); menu.AddSeparator(""); // Actions if only one node is selected if (Selection.objects.Length == 1 && Selection.activeObject is XNode.NodeGraphComment) { XNode.NodeGraphComment comment = Selection.activeObject as XNode.NodeGraphComment; menu.AddItem(new GUIContent("Rename"), false, NodeEditorWindow.current.RenameSelectedComment); } // Add actions to any number of selected nodes menu.AddItem(new GUIContent("Duplicate"), false, NodeEditorWindow.current.DuplicateSelectedNodes); menu.AddItem(new GUIContent("Remove"), false, NodeEditorWindow.current.RemoveSelectedNodes); } public virtual Color GetTypeColor(Type type) { return NodeEditorPreferences.GetTypeColor(type); } /// Create a node and save it in the graph asset public virtual void CreateNode(Type type, Vector2 position) { XNode.Node node = target.AddNode(type); node.position = position; node.name = UnityEditor.ObjectNames.NicifyVariableName(type.Name); AssetDatabase.AddObjectToAsset(node, target); if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets(); NodeEditorWindow.RepaintAll(); } /// Creates a copy of the original node in the graph public XNode.Node CopyNode(XNode.Node original) { XNode.Node node = target.CopyNode(original); node.name = original.name; AssetDatabase.AddObjectToAsset(node, target); if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets(); return node; } /// Safely remove a node and all its connections. public void RemoveNode(XNode.Node node) { UnityEngine.Object.DestroyImmediate(node, true); target.RemoveNode(node); if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets(); } /// Create a comment and save it in the graph asset public void CreateComment(Vector2 position) { XNode.NodeGraphComment comment = target.AddComment(); comment.position = position; comment.comment = "New comment"; AssetDatabase.AddObjectToAsset(comment, target); if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets(); NodeEditorWindow.RepaintAll(); } /// Creates a copy of the original comment in the graph public XNode.NodeGraphComment CopyComment(XNode.NodeGraphComment original) { XNode.NodeGraphComment comment = target.CopyComment(original); comment.name = original.name; AssetDatabase.AddObjectToAsset(comment, target); if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets(); return comment; } /// Safely remove a comment public void RemoveComment(XNode.NodeGraphComment comment) { UnityEngine.Object.DestroyImmediate(comment, true); target.RemoveComment(comment); if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets(); } [AttributeUsage(AttributeTargets.Class)] public class CustomNodeGraphEditorAttribute : Attribute, XNodeEditor.Internal.NodeEditorBase.INodeEditorAttrib { private Type inspectedType; public string editorPrefsKey; /// Tells a NodeGraphEditor which Graph type it is an editor for /// Type that this editor can edit /// Define unique key for unique layout settings instance public CustomNodeGraphEditorAttribute(Type inspectedType, string editorPrefsKey = "xNode.Settings") { this.inspectedType = inspectedType; this.editorPrefsKey = editorPrefsKey; } public Type GetInspectedType() { return inspectedType; } } } }