diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs index f3ebde4..9424155 100644 --- a/Scripts/Editor/NodeEditorAction.cs +++ b/Scripts/Editor/NodeEditorAction.cs @@ -6,7 +6,7 @@ using UnityEngine; namespace XNodeEditor { public partial class NodeEditorWindow { - public enum NodeActivity { Idle, HoldNode, DragNode, HoldGrid, DragGrid } + public enum NodeActivity { Idle, HoldNode, DragNode, HoldGrid, DragGrid, HoldComment, ResizeComment } public static NodeActivity currentActivity = NodeActivity.Idle; public static bool isPanning { get; private set; } public static Vector2[] dragOffset; @@ -15,11 +15,17 @@ namespace XNodeEditor { private bool IsHoveringPort { get { return hoveredPort != null; } } private bool IsHoveringNode { get { return hoveredNode != null; } } private bool IsHoveringReroute { get { return hoveredReroute.port != null; } } + private bool IsHoveringComment { get { return hoveredComment != null; } } + private bool IsResizingComment { get { return resizingComment != null; } } private XNode.Node hoveredNode = null; [NonSerialized] private XNode.NodePort hoveredPort = null; [NonSerialized] private XNode.NodePort draggedOutput = null; [NonSerialized] private XNode.NodePort draggedOutputTarget = null; [NonSerialized] private List draggedOutputReroutes = new List(); + [NonSerialized] private XNode.NodeGraphComment hoveredComment = null; + [NonSerialized] private XNode.NodeGraphComment resizingComment = null; + public enum NodeGraphCommentSide { Top, TopRight, Right, BottomRight, Bottom, BottomLeft, Left, TopLeft } + public static NodeGraphCommentSide resizingCommentSide; private RerouteReference hoveredReroute = new RerouteReference(); private List selectedReroutes = new List(); private Rect nodeRects; @@ -68,7 +74,7 @@ namespace XNodeEditor { draggedOutputTarget = null; } Repaint(); - } else if (currentActivity == NodeActivity.HoldNode) { + } else if (currentActivity == NodeActivity.HoldNode || currentActivity == NodeActivity.HoldComment) { RecalculateDragOffsets(e); currentActivity = NodeActivity.DragNode; Repaint(); @@ -110,6 +116,15 @@ namespace XNodeEditor { } } } + else if(Selection.objects[i] is XNode.NodeGraphComment) { + XNode.NodeGraphComment comment = Selection.objects[i] as XNode.NodeGraphComment; + Vector2 initial = comment.position; + comment.position = mousePos + dragOffset[i]; + if (gridSnap) { + comment.position.x = (Mathf.Round((comment.position.x + 8) / 16) * 16) - 8; + comment.position.y = (Mathf.Round((comment.position.y + 8) / 16) * 16) - 8; + } + } } // Move selected reroutes with offset for (int i = 0; i < selectedReroutes.Count; i++) { @@ -133,6 +148,44 @@ namespace XNodeEditor { if (boxSize.x < 0) { boxStartPos.x += boxSize.x; boxSize.x = Mathf.Abs(boxSize.x); } if (boxSize.y < 0) { boxStartPos.y += boxSize.y; boxSize.y = Mathf.Abs(boxSize.y); } selectionBox = new Rect(boxStartPos, boxSize); + Repaint(); + } else if (currentActivity == NodeActivity.ResizeComment) { + switch (resizingCommentSide) { + case NodeGraphCommentSide.Top: + resizingComment.size.y -= e.delta.y; + resizingComment.position.y += e.delta.y; + break; + case NodeGraphCommentSide.TopRight: + resizingComment.size.y -= e.delta.y; + resizingComment.position.y += e.delta.y; + resizingComment.size.x += e.delta.x; + break; + case NodeGraphCommentSide.Right: + resizingComment.size.x += e.delta.x; + break; + case NodeGraphCommentSide.BottomRight: + resizingComment.size += e.delta; + break; + case NodeGraphCommentSide.Bottom: + resizingComment.size.y += e.delta.y; + break; + case NodeGraphCommentSide.BottomLeft: + resizingComment.size.x -= e.delta.x; + resizingComment.position.x += e.delta.x; + resizingComment.size.y += e.delta.y; + break; + case NodeGraphCommentSide.Left: + resizingComment.size.x -= e.delta.x; + resizingComment.position.x += e.delta.x; + break; + case NodeGraphCommentSide.TopLeft: + resizingComment.size.x -= e.delta.x; + resizingComment.position.x += e.delta.x; + resizingComment.size.y -= e.delta.y; + resizingComment.position.y += e.delta.y; + break; + } + Repaint(); } } else if (e.button == 1 || e.button == 2) { @@ -191,8 +244,33 @@ namespace XNodeEditor { e.Use(); currentActivity = NodeActivity.HoldNode; } + else if (IsHoveringComment && !IsHoveringNode) { + if (!Selection.Contains(hoveredComment)) { + if (e.shift) { + SelectComment(hoveredComment, true); + SelectNodesInComment(hoveredComment); + } + else SelectComment(hoveredComment, e.control || e.shift); + } + else if (e.control || e.shift) { + DeselectComment(hoveredComment); + if (e.shift) + { + DeselectNodesInComment(hoveredComment); + } + } else SelectComment(hoveredComment, false); + + e.Use(); + currentActivity = NodeActivity.HoldComment; + } else if (IsResizingComment && !e.control && !e.shift) { + selectedReroutes.Clear(); + Selection.activeObject = null; + + e.Use(); + currentActivity = NodeActivity.ResizeComment; + } // If mousedown on grid background, deselect all - else if (!IsHoveringNode) { + else if (!IsHoveringNode && !IsHoveringComment) { currentActivity = NodeActivity.HoldGrid; if (!e.control && !e.shift) { selectedReroutes.Clear(); @@ -227,7 +305,7 @@ namespace XNodeEditor { IEnumerable nodes = Selection.objects.Where(x => x is XNode.Node).Select(x => x as XNode.Node); foreach (XNode.Node node in nodes) EditorUtility.SetDirty(node); if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets(); - } else if (!IsHoveringNode) { + } else if (!IsHoveringNode && !IsHoveringComment && !IsResizingComment) { // If click outside node, release field focus if (!isPanning) { EditorGUI.FocusTextInControl(null); @@ -265,6 +343,11 @@ namespace XNodeEditor { GenericMenu menu = new GenericMenu(); NodeEditor.GetEditor(hoveredNode).AddContextMenuItems(menu); menu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero)); + } else if (IsHoveringComment && !IsHoveringNode) { + if (!Selection.Contains(hoveredComment)) SelectComment(hoveredComment, false); + GenericMenu menu = new GenericMenu(); + graphEditor.AddCommentContextMenuItems(menu); + menu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero)); } else if (!IsHoveringNode) { GenericMenu menu = new GenericMenu(); graphEditor.AddContextMenuItems(menu); @@ -315,6 +398,11 @@ namespace XNodeEditor { XNode.Node node = Selection.objects[i] as XNode.Node; dragOffset[i] = node.position - WindowToGridPosition(current.mousePosition); } + else if (Selection.objects[i] is XNode.NodeGraphComment) + { + XNode.NodeGraphComment comment = Selection.objects[i] as XNode.NodeGraphComment; + dragOffset[i] = comment.position - WindowToGridPosition(current.mousePosition); + } } // Selected reroutes @@ -342,6 +430,10 @@ namespace XNodeEditor { XNode.Node node = item as XNode.Node; graphEditor.RemoveNode(node); } + else if (item is XNode.NodeGraphComment) { + XNode.NodeGraphComment comment = item as XNode.NodeGraphComment; + graphEditor.RemoveComment(comment); + } } } @@ -353,6 +445,15 @@ namespace XNodeEditor { } } + /// Initiate a rename on the currently selected comment + public void RenameSelectedComment() + { + if (Selection.objects.Length == 1 && Selection.activeObject is XNode.NodeGraphComment) + { + renamingComment = Selection.activeObject as XNode.NodeGraphComment; + } + } + /// Draw this node on top of other nodes by placing it last in the graph.nodes list public void MoveNodeToTop(XNode.Node node) { int index; @@ -374,6 +475,12 @@ namespace XNodeEditor { substitutes.Add(srcNode, newNode); newNode.position = srcNode.position + new Vector2(30, 30); newNodes[i] = newNode; + } else if (Selection.objects[i] is XNode.NodeGraphComment) { + XNode.NodeGraphComment srcComment = Selection.objects[i] as XNode.NodeGraphComment; + if (srcComment.graph != graph) continue; // ignore comments selected in another graph + XNode.NodeGraphComment newComment = graphEditor.CopyComment(srcComment); + newComment.position = srcComment.position + new Vector2(30, 30); + newNodes[i] = newComment; } } diff --git a/Scripts/Editor/NodeEditorGUI.cs b/Scripts/Editor/NodeEditorGUI.cs index 9f48f0e..9d0663b 100644 --- a/Scripts/Editor/NodeEditorGUI.cs +++ b/Scripts/Editor/NodeEditorGUI.cs @@ -13,6 +13,8 @@ namespace XNodeEditor { private int topPadding { get { return isDocked() ? 19 : 22; } } /// Executed after all other window GUI. Useful if Zoom is ruining your day. Automatically resets after being run. public event Action onLateGUI; + public XNode.NodeGraphComment renamingComment; + public bool renamingStarted = false; private void OnGUI() { Event e = Event.current; @@ -23,7 +25,13 @@ namespace XNodeEditor { Controls(); + if (e.type == EventType.Layout) + { + selectionCache = new List(Selection.objects); + } + DrawGrid(position, zoom, panOffset); + DrawComments(); DrawConnections(); DrawDraggedConnection(); DrawNodes(); @@ -237,9 +245,6 @@ namespace XNodeEditor { private void DrawNodes() { Event e = Event.current; - if (e.type == EventType.Layout) { - selectionCache = new List(Selection.objects); - } //Active node is hashed before and after node GUI to detect changes int nodeHash = 0; @@ -419,5 +424,165 @@ namespace XNodeEditor { Repaint(); } } + + private void DrawComments() + { + Event e = Event.current; + + BeginZoomed(position, zoom, topPadding); + Vector2 mousePos = Event.current.mousePosition; + + if (e.type != EventType.Layout) { + hoveredComment = null; + if (currentActivity != NodeActivity.ResizeComment) resizingComment = null; + } + + List preSelection = preBoxSelection != null ? new List(preBoxSelection) : new List(); + + Vector2 boxStartPos = GridToWindowPositionNoClipped(dragBoxStart); + Vector2 boxSize = mousePos - boxStartPos; + if (boxSize.x < 0) { boxStartPos.x += boxSize.x; boxSize.x = Mathf.Abs(boxSize.x); } + if (boxSize.y < 0) { boxStartPos.y += boxSize.y; boxSize.y = Mathf.Abs(boxSize.y); } + Rect selectionBox = new Rect(boxStartPos, boxSize); + + for (int n = 0; n < graph.comments.Count; n++) { + XNode.NodeGraphComment comment = graph.comments[n]; + if (comment == null) continue; + + Vector2 commentPos = GridToWindowPositionNoClipped(comment.position); + GUILayout.BeginArea(new Rect(commentPos, new Vector2(comment.size.x, comment.size.y))); + + bool selected = selectionCache.Contains(comment); + + if (selected) { + GUIStyle style = new GUIStyle(NodeEditorResources.styles.commentBody); + GUIStyle highlightStyle = new GUIStyle(NodeEditorResources.styles.nodeHighlight); + highlightStyle.padding = style.padding; + style.padding = new RectOffset(); + GUI.color = Color.white; + GUILayout.BeginVertical(style); + GUI.color = NodeEditorPreferences.GetSettings().highlightColor; + GUILayout.BeginVertical(new GUIStyle(highlightStyle)); + } else { + GUIStyle style = new GUIStyle(NodeEditorResources.styles.commentBody); + GUI.color = Color.white; + GUILayout.BeginVertical(style); + } + + if (renamingComment == comment) { + if (Selection.Contains(renamingComment)) { + int controlID = EditorGUIUtility.GetControlID(FocusType.Keyboard) + 1; + if (!renamingStarted) { + EditorGUIUtility.keyboardControl = controlID; + EditorGUIUtility.editingTextField = true; + renamingStarted = true; + } + comment.comment = EditorGUILayout.TextField(comment.comment, NodeEditorResources.styles.commentHeader, GUILayout.Height(26)); + if (!EditorGUIUtility.editingTextField) { + Debug.Log("Finish renaming"); + renamingComment = null; + renamingStarted = false; + } + } + else { + // Selection changed, so stop renaming. + GUILayout.Label(comment.comment, NodeEditorResources.styles.commentHeader, GUILayout.Height(26)); + renamingComment = null; + renamingStarted = false; + } + } else { + GUIStyle blackStyle = new GUIStyle(NodeEditorResources.styles.commentHeader); + blackStyle.normal.textColor = new Color(0.2f, 0.2f, 0.2f); + + GUILayout.Label(comment.comment, blackStyle, GUILayout.Height(26)); + + Rect lastRect = GUILayoutUtility.GetLastRect(); + lastRect.x -= 0.5f; + lastRect.y -= 1; + GUI.Label(lastRect, comment.comment, NodeEditorResources.styles.commentHeader); + } + + + GUILayout.FlexibleSpace(); + + GUILayout.EndVertical(); + + if (selected) GUILayout.EndVertical(); + + if (e.type != EventType.Layout && currentActivity != NodeActivity.ResizeComment) { + //Check if we are hovering this node + Vector2 commentSize = GUILayoutUtility.GetLastRect().size; + Rect windowRect = new Rect(commentPos, commentSize); + + float padding = 12; + + // Resizing areas + // Follows the NodeGraphCommentSide order + Rect[] resizeRects = new[] { + new Rect(padding, 0, commentSize.x - padding * 2, padding), + new Rect(commentSize.x - padding, 0, padding, padding), + new Rect(commentSize.x - padding, padding, padding, commentSize.y - padding * 2), + new Rect(commentSize.x - padding, commentSize.y - padding, padding, padding), + new Rect(padding, commentSize.y - padding, commentSize.x - padding * 2, padding), + new Rect(0, commentSize.y - padding, padding, padding), + new Rect(0, padding, padding, commentSize.y - padding * 2), + new Rect(0, 0, padding, padding), + }; + + // Icons for the resize area list + MouseCursor[] resizeIcons = new[] { + MouseCursor.ResizeVertical, + MouseCursor.ResizeUpRight, + MouseCursor.ResizeHorizontal, + MouseCursor.ResizeUpLeft, + MouseCursor.ResizeVertical, + MouseCursor.ResizeUpRight, + MouseCursor.ResizeHorizontal, + MouseCursor.ResizeUpLeft, + }; + + for (int i = 0; i < resizeRects.Length; i++) { + EditorGUIUtility.AddCursorRect(resizeRects[i], resizeIcons[i]); + + // Transform the locations now to gui space locations + resizeRects[i].position += commentPos; + } + + if (windowRect.Contains(mousePos)) { + //If dragging a selection box, add nodes inside to selection + if (currentActivity == NodeActivity.DragGrid) { + if (windowRect.Overlaps(selectionBox)) preSelection.Add(comment); + } else { + // Check if we should resize or select + bool resizeAreaClicked = false; + for (int i = 0; i < resizeRects.Length; i++) { + if (resizeRects[i].Contains(mousePos)) { + resizingComment = comment; + // i can be cast to NodeGraphCommentSide as resizeRects + // has one element per NodeGraphCommentSide value and + // uses the same order + resizingCommentSide = (NodeGraphCommentSide)i; + resizeAreaClicked = true; + + break; + } + } + + if (!resizeAreaClicked) hoveredComment = comment; + } + } + + //If dragging a selection box, add nodes inside to selection + if (currentActivity == NodeActivity.DragGrid) { + if (windowRect.Overlaps(selectionBox)) preSelection.Add(comment); + } + } + + GUILayout.EndArea(); + } + + if (e.type != EventType.Layout && currentActivity == NodeActivity.DragGrid) Selection.objects = preSelection.ToArray(); + EndZoomed(position, zoom, topPadding); + } } } \ No newline at end of file diff --git a/Scripts/Editor/NodeEditorResources.cs b/Scripts/Editor/NodeEditorResources.cs index 0a84e0a..18e803b 100644 --- a/Scripts/Editor/NodeEditorResources.cs +++ b/Scripts/Editor/NodeEditorResources.cs @@ -12,14 +12,16 @@ namespace XNodeEditor { private static Texture2D _nodeBody; public static Texture2D nodeHighlight { get { return _nodeHighlight != null ? _nodeHighlight : _nodeHighlight = Resources.Load("xnode_node_highlight"); } } private static Texture2D _nodeHighlight; + public static Texture2D commentBody { get { return _commentBody != null ? _commentBody : _commentBody = Resources.Load("xnode_comment"); } } + private static Texture2D _commentBody; // Styles public static Styles styles { get { return _styles != null ? _styles : _styles = new Styles(); } } public static Styles _styles = null; public static GUIStyle OutputPort { get { return new GUIStyle(EditorStyles.label) { alignment = TextAnchor.UpperRight }; } } public class Styles { - public GUIStyle inputPort, nodeHeader, nodeBody, tooltip, nodeHighlight; - + public GUIStyle inputPort, nodeHeader, nodeBody, tooltip, nodeHighlight, commentHeader, commentBody; + public Styles() { GUIStyle baseStyle = new GUIStyle("Label"); baseStyle.fixedHeight = 18; @@ -44,6 +46,17 @@ namespace XNodeEditor { tooltip = new GUIStyle("helpBox"); tooltip.alignment = TextAnchor.MiddleCenter; + + commentHeader = new GUIStyle(); + commentHeader.alignment = TextAnchor.MiddleCenter; + commentHeader.fontStyle = FontStyle.Bold; + commentHeader.fontSize = 20; + commentHeader.normal.textColor = new Color(0.93f, 0.93f, 0.93f); + + commentBody = new GUIStyle(); + commentBody.normal.background = NodeEditorResources.commentBody; + commentBody.border = new RectOffset(32, 32, 32, 32); + commentBody.padding = new RectOffset(16, 16, 4, 16); } } diff --git a/Scripts/Editor/NodeEditorWindow.cs b/Scripts/Editor/NodeEditorWindow.cs index 72fd4ce..829377b 100644 --- a/Scripts/Editor/NodeEditorWindow.cs +++ b/Scripts/Editor/NodeEditorWindow.cs @@ -142,6 +142,46 @@ namespace XNodeEditor { Selection.objects = selection.ToArray(); } + public void SelectComment(XNode.NodeGraphComment comment, bool add) { + if (add) { + List selection = new List(Selection.objects); + selection.Add(comment); + Selection.objects = selection.ToArray(); + } else Selection.objects = new Object[] { comment }; + } + + public void DeselectComment(XNode.NodeGraphComment comment) { + List selection = new List(Selection.objects); + selection.Remove(comment); + Selection.objects = selection.ToArray(); + } + + public void SelectNodesInComment(XNode.NodeGraphComment comment) { + Rect commentRect = new Rect(comment.position, comment.size); + for (int i = 0; i < graph.nodes.Count; i++) { + XNode.Node node = graph.nodes[i]; + if (!node) continue; + + if (commentRect.Contains(node.position)) SelectNode(node, true); + } + } + + public void DeselectNodesInComment(XNode.NodeGraphComment comment) + { + List selection = new List(Selection.objects); + + + Rect commentRect = new Rect(comment.position, comment.size); + for (int i = 0; i < graph.nodes.Count; i++) { + XNode.Node node = graph.nodes[i]; + if (!node) continue; + + if (commentRect.Contains(node.position)) selection.Remove(node); + } + + Selection.objects = selection.ToArray(); + } + [OnOpenAsset(0)] public static bool OnOpen(int instanceID, int line) { XNode.NodeGraph nodeGraph = EditorUtility.InstanceIDToObject(instanceID) as XNode.NodeGraph; diff --git a/Scripts/Editor/NodeGraphEditor.cs b/Scripts/Editor/NodeGraphEditor.cs index 20f5657..db76c51 100644 --- a/Scripts/Editor/NodeGraphEditor.cs +++ b/Scripts/Editor/NodeGraphEditor.cs @@ -53,10 +53,41 @@ namespace XNodeEditor { }); } 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); } @@ -87,6 +118,32 @@ namespace XNodeEditor { 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 { diff --git a/Scripts/Editor/Resources/xnode_comment.png b/Scripts/Editor/Resources/xnode_comment.png new file mode 100644 index 0000000..f9284e3 Binary files /dev/null and b/Scripts/Editor/Resources/xnode_comment.png differ diff --git a/Scripts/Editor/Resources/xnode_comment.png.meta b/Scripts/Editor/Resources/xnode_comment.png.meta new file mode 100644 index 0000000..f49a081 --- /dev/null +++ b/Scripts/Editor/Resources/xnode_comment.png.meta @@ -0,0 +1,98 @@ +fileFormatVersion: 2 +guid: 043280fd1ad23004487ead415d2b11dd +timeCreated: 1507454532 +licenseType: Free +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 4 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 0 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: 1 + mipBias: -1 + wrapU: 1 + wrapV: 1 + wrapW: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + platformSettings: + - buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: Standalone + maxTextureSize: 2048 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: Android + maxTextureSize: 2048 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: WebGL + maxTextureSize: 2048 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor/Resources/xnode_node_comment_workfile.psd b/Scripts/Editor/Resources/xnode_node_comment_workfile.psd new file mode 100644 index 0000000..e439523 Binary files /dev/null and b/Scripts/Editor/Resources/xnode_node_comment_workfile.psd differ diff --git a/Scripts/Editor/Resources/xnode_node_comment_workfile.psd.meta b/Scripts/Editor/Resources/xnode_node_comment_workfile.psd.meta new file mode 100644 index 0000000..930c2e6 --- /dev/null +++ b/Scripts/Editor/Resources/xnode_node_comment_workfile.psd.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: 18f697c0521665f48b9b797ec72852e8 +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 7 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + wrapU: -1 + wrapV: -1 + wrapW: -1 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + platformSettings: + - serializedVersion: 2 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/NodeGraph.cs b/Scripts/NodeGraph.cs index 6b95fc2..5abf274 100644 --- a/Scripts/NodeGraph.cs +++ b/Scripts/NodeGraph.cs @@ -10,6 +10,9 @@ namespace XNode { /// All nodes in the graph. /// See: [SerializeField] public List nodes = new List(); + /// All comments in the graph. + /// See: + [SerializeField] public List comments = new List(); /// Add a node to the graph by type public T AddNode() where T : Node { @@ -53,6 +56,29 @@ namespace XNode { nodes.Clear(); } + /// Add a comment to the graph + public NodeGraphComment AddComment() { + NodeGraphComment comment = ScriptableObject.CreateInstance(); + comment.graph = this; + comments.Add(comment); + + return comment; + } + + /// Creates a copy of the comment node in the graph + public virtual NodeGraphComment CopyComment(NodeGraphComment original) { + NodeGraphComment comment = ScriptableObject.Instantiate(original); + comment.graph = this; + comments.Add(comment); + return comment; + } + + /// Safely remove a comment + public void RemoveComment(NodeGraphComment comment) { + comments.Remove(comment); + if (Application.isPlaying) Destroy(comment); + } + /// Create a new deep copy of this graph public XNode.NodeGraph Copy() { // Instantiate a new nodegraph instance diff --git a/Scripts/NodeGraphComment.cs b/Scripts/NodeGraphComment.cs new file mode 100644 index 0000000..47ae11b --- /dev/null +++ b/Scripts/NodeGraphComment.cs @@ -0,0 +1,14 @@ +using System; +using UnityEngine; + +namespace XNode +{ + [Serializable] + public class NodeGraphComment : ScriptableObject + { + [SerializeField] public NodeGraph graph; + [SerializeField] public Vector2 position; + [SerializeField] public Vector2 size = new Vector2(200, 300); + [SerializeField] public string comment; + } +} \ No newline at end of file diff --git a/Scripts/NodeGraphComment.cs.meta b/Scripts/NodeGraphComment.cs.meta new file mode 100644 index 0000000..d0f4740 --- /dev/null +++ b/Scripts/NodeGraphComment.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c8a458777eab4f84287de1d8a542972c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: